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/*.ymland 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,
jqfor 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,offare booleans in YAML 1.1.country: NObecomescountry: false. YAML 1.2 narrowed booleans totrue/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.loadwithout 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)