YAML Cheatsheet
Mappings and Sequences
Use this YAML reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
The two collection types
A mapping is key: value pairs. A sequence is - item lines. Nesting is by
indentation, and structure comes from spaces only.
name: api replicas: 3 labels: app: api tier: backend ports: - 8080 - 9090
parses to:
{"name": "api", "replicas": 3,
"labels": {"app": "api", "tier": "backend"},
"ports": [8080, 9090]}Indentation rules
- Spaces only. A tab in the indentation is a syntax error, not a warning.
a:\n\tb: 1 fails with "found character that cannot start any token".
- Two spaces per level is the convention. Any consistent count works, but it must
be consistent within a block.
- A key with no value is null, not an empty map: a: gives {"a": null}.
For an empty map or list write a: {} or a: [].
- Blank lines and trailing spaces are ignored between entries.
# both of these are illegal bad_tab: nested: 1 # TAB, error bad_dedent: a: 1 b: 2 # inconsistent indent, error
Sequences under a key may or may not be indented
Both forms below parse identically. The - is allowed to sit at the parent key's
own column, which reads oddly but is correct YAML.
# form A, indented ports: - 8080 - 9090 # form B, flush with the key ports: - 8080 - 9090
Both give {"ports": [8080, 9090]}. Kubernetes manifests use form A, Ansible
playbooks and docker-compose.yml often use form B. Do not "fix" one into the
other in a review, and never mix them inside one block.
Mappings inside sequences
This is the alignment people get wrong. The first key sits on the same line as
the -, and every sibling key aligns to that key's column, not to the dash.
containers:
- name: api
image: api:1.2
ports:
- containerPort: 8080
- name: sidecar
image: envoy:1.29parses to:
{"containers": [
{"name": "api", "image": "api:1.2",
"ports": [{"containerPort": 8080}]},
{"name": "sidecar", "image": "envoy:1.29"}]}Getting image one space off puts it in the wrong dict or errors out. If your
editor shows indent guides, line them up on the n of name, not the -.
Nesting patterns
| Shape | YAML |
|---|---|
| map of maps | a: then indented b: 1 |
| map of lists | a: then - 1 |
| list of maps | - a: 1 then aligned b: 2 |
| list of lists | - - 1 then aligned - 2 |
| deep map | a: b: c: each one level deeper |
matrix: - - 1 - 2 - - 3
gives {"matrix": [[1, 2], [3]]}. The - - on one line is the only compact way
to open a nested sequence in block style. In practice reach for flow style
(matrix: [[1, 2], [3]]) instead.
Keys
Keys are scalars, so they get resolved like any other scalar. That means a key can be an int, a bool, or null, which is almost never what you want.
1: one # integer key 1 "1": one # string key "1" on: yes # PyYAML: key True, value True a b: 1 # string key "a b", plain is fine "a: b": 1 # quoted because of the colon-space
Duplicate keys are where parsers split. PyYAML silently keeps the last one,
ruamel raises DuplicateKeyError, and yaml.v3 errors too.
port: 8080 port: 9090 # PyYAML -> {"port": 9090}, ruamel -> error
A duplicated key in a long manifest silently discards the earlier value, so run a
linter (yamllint flags it as key-duplicates) rather than trusting the parser.
Complex keys
? key / : value lets a key be a whole collection. It parses structurally but
most target languages cannot hold a list as a dict key.
? [Manchester, Sheffield]
: 191PyYAML raises ConstructorError (unhashable list). You will effectively never
need this, but you should recognise the ? so you do not mistake it for a typo.