YAML#

YAML (“YAML Ain’t Markup Language”) is a human-friendly data serialization format. Every JSON document is also a valid YAML document, but YAML adds indentation-based structure, comments, multi-line strings, anchors, and typed scalars.

YAML is the dominant configuration format for Kubernetes, Ansible, GitHub Actions, GitLab CI, Helm, and most cloud-native tooling.

Example#

# A config file
server:
  host: 0.0.0.0
  port: 8080
  tls:
    cert: /etc/ssl/cert.pem
    key:  /etc/ssl/key.pem

features:
  - search
  - billing
  - notifications

limits:
  max_connections: 1024
  timeout_ms: 5000

Types#

YAML’s three core types map cleanly onto JSON’s six (with dates as a YAML addition). The catch is YAML’s auto-typing of scalars; the parser tries to infer types from the unquoted text, which produces classic surprises like no becoming a boolean.

  • Scalars, strings, numbers, booleans, null, dates.

  • Sequences, ordered lists.

  • Mappings, key/value collections.

YAML auto-types many scalars.

string:  hello
int:     42
float:   3.14
bool:    true       # also: True, TRUE, yes, on (per spec, but YAML 1.2 narrows this)
null:    null       # also: ~ , empty
date:    2026-04-26
datetime: 2026-04-26T12:00:00Z

To force a string, quote it.

yes_str: "yes"           # otherwise parsed as boolean by some parsers
version: "1.10"          # otherwise parsed as float 1.1
zip:     "01234"         # otherwise parsed as int 1234

Strings#

YAML offers three string styles, each with different escaping rules. Plain is unquoted (limited character set); single-quoted is literal text (escape ' by doubling); double-quoted supports the full set of backslash escapes. For multi-line content, the literal (|) and folded (>) styles cover most needs.

  • Plain, unquoted; restricted character set.

  • Single-quoted, preserves literal text; escape ' by doubling.

  • Double-quoted, supports backslash escapes.

Multi-line strings.

literal: |
  line one
  line two
  keeps newlines

folded: >
  this becomes
  a single line
  with spaces

Use |- / >- to strip the trailing newline; |+ / >+ to keep multiple trailing newlines.

Sequences and Mappings#

YAML supports two notations for sequences and mappings. indented block style for human-readable config and JSON-like flow style for compact one-liners. Most config files use block; flow shows up in inline values.

Block style.

tags:
  - red
  - green
  - blue

user:
  name: operator
  age:  36

Flow style (JSON-like).

tags: [red, green, blue]
user: { name: operator, age: 36 }

Anchors and Aliases#

Re-use blocks with anchors (&name) and aliases (*name), YAML’s solution to “I want to share this config block in three places without copy-paste.” The merge key (<<:) is the YAML 1.1 way to extend a referenced block; YAML 1.2 doesn’t standardize it, but most config tools (Kubernetes, Helm, Ansible) support it anyway.

defaults: &defaults
  adapter: postgres
  pool:    5
  timeout: 5000

development:
  <<: *defaults                 # YAML 1.1 merge key
  database: app_dev

production:
  <<: *defaults
  database: app_prod
  host: db.prod.example.com

The merge key << is a YAML 1.1 feature; some YAML 1.2 parsers don’t support it natively. Most config tools (Kubernetes, Helm, Ansible) do.

Documents#

A single YAML file can hold multiple documents separated by ---. The pattern is everywhere in Kubernetes; a single manifest file commonly defines a Service, a Deployment, and a ConfigMap as separate documents in the same file.

---
apiVersion: v1
kind: Service
metadata: { name: web }
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }

Schemas#

YAML doesn’t have a native schema language, so the ecosystem borrows from JSON Schema (most editors and CI linters do exactly this) or steps up to a typed language like Cue. The mainstream options.

  • JSON Schema works for YAML; tooling converts to JSON internally.

  • Cue, a typed superset designed for YAML/JSON validation and templating.

  • Schemastore, VS Code and other editors auto-validate .github/workflows/*.yml and many other well-known YAML files.

Tooling#

The day-to-day YAML toolkit. yq is the closest analog to jq for YAML; yamllint catches the formatting issues most parsers won’t; Helm and Kustomize are the YAML templating tools that dominate Kubernetes deployments.

  • yq, jq for YAML.

    $ yq '.server.port' config.yaml
    $ yq '.users[] | select(.role == "admin") | .email' users.yaml
    $ yq -i '.image = "myorg/app:1.2.3"' deploy.yaml
    
  • kubectl explain, documents Kubernetes YAML schemas.

  • yamllint, linter.

  • Helm, Kustomize – template-and-overlay tools that produce YAML.

Pitfalls#

YAML’s flexibility is its biggest source of surprise bugs. The five pitfalls below cover the bulk of “this config used to work” failures: implicit type coercion, indentation errors, version-format mistakes, and YAML 1.1 vs 1.2 parser differences.

  • The Norway problem, no, yes, on, off are booleans in YAML 1.1. country: NO becomes country: false. YAML 1.2 narrowed booleans to true / false, but parsers vary. Quote strings that could be misinterpreted.

  • Indentation matters, two spaces is the convention; tabs are disallowed by most parsers. Inconsistent indentation produces obscure errors.

  • Implicit typing of versions, leading zeros, hex numbers. Quote whenever in doubt.

  • Flow-style commas are easy to miss; trailing commas are not allowed.

  • YAML 1.1 vs 1.2, most parsers default to 1.1.2 / 1.2 in 2026; a few legacy ones still differ.

Where YAML Wins#

The cases where YAML’s human-readability and structure pay off. Configuration files, multi-document manifests, and DRY config all benefit from comments, anchors, and indented structure that JSON can’t offer.

  • Configuration that humans edit by hand.

  • Files that benefit from comments.

  • Multi-document files (Kubernetes manifests).

  • Anchors / merges for DRY config.

Where YAML Loses#

YAML’s flexibility costs it in three specific contexts. Wire formats want JSON’s predictability; untrusted-input parsers want JSON’s simpler attack surface; cross-editor work wants formats that don’t depend on indentation fidelity.

  • Wire formats, YAML’s ambiguity makes parsers slow and surprising. Use JSON.

  • Untrusted input, some YAML parsers default to types that can execute code (Python’s yaml.load without safe=True is a classic CVE source). Always use the safe-loader.

  • Anything where indentation reliability matters across editors.

Workflow#

Extract, parse, filter, save. yq (Mike Farah’s Go version) is the daily-driver CLI; Python ruamel.yaml for round-trips that preserve comments.

Extract and inspect.

$ yq . config.yaml | head
$ yq '.services | keys' docker-compose.yml
$ yq 'length' users.yaml

Parse and filter.

# values of one key
$ yq '.services[].image' docker-compose.yml

# filter to entries that match a predicate
$ yq '.users[] | select(.role == "admin")' users.yaml

# project a subset of fields
$ yq '.users[] | {name, email}' users.yaml

Save.

$ yq '[.users[] | select(.role == "admin")]' users.yaml > admins.yaml
$ yq -i '.image = "myorg/app:1.1"' docker-compose.yml      # in-place

Python preserving comments.

from ruamel.yaml import YAML
y = YAML(); y.preserve_quotes = True
with open("users.yaml") as f: data = y.load(f)
data["users"] = [u for u in data["users"] if u["role"] == "admin"]
with open("admins.yaml", "w") as f: y.dump(data, f)