Cheatsheet

YAML Syntax Cheatsheet

YAML is an indentation-based data format used for Docker Compose files, Kubernetes manifests, CI pipelines and many other config files. This sheet is for developers who write or debug YAML by hand. The confusion it clears up: a bare word such as NO, yes or 0755 can silently become a boolean or a number, and the answer depends on whether your parser follows YAML 1.1 or 1.2. Outputs below come from PyYAML 6.0, which follows 1.1 rules, and ruamel.yaml 0.19.1, which follows 1.2.

Quick reference

Structure

Construct Syntax Parsed as
Mapping `name: web` `{"name": "web"}`
Nested mapping `env:` then an indented `PORT: 80` Object inside object
Sequence `- a` on each line List
Sequence in a mapping `tags:` then `- api` `{"tags": ["api"]}`
Flow sequence `[api, public]` List
Flow mapping `{cpu: 500m, memory: 256Mi}` Object
Comment `# text` Ignored
Empty collections `[]` and `{}` Empty list, empty object
Document start and end `---` and `...` Separates documents in one stream

Indentation uses spaces. The spec says tab characters must not be used for indentation, and there is no required width, but every line of one block must use the same number of spaces.

Scalar types in YAML 1.2 core schema

Written as Resolves to
`null`, `Null`, `NULL`, `~`, or an empty value null
`true`, `True`, `TRUE`, `false`, `False`, `FALSE` boolean
`42`, `-17`, `+3` integer
`0o17` integer 15 in octal
`0x1F` integer 31 in hexadecimal
`3.14`, `.5`, `1e3`, `6.02e+23` float
`.inf`, `-.inf`, `.nan` float special values
anything else string

Same text, different parser

Input PyYAML (1.1 rules) ruamel.yaml (1.2 rules)
`NO` `False` `'NO'`
`yes`, `on` `True` `'yes'`, `'on'`
`0755` `493` `755`
`1:30` `90` `'1:30'`
`1e3` `'1e3'` (a string) `1000.0`
`0o17` `'0o17'` (a string) `15`
`1.10` `1.1` `1.1`
`a: 1` repeated key last value wins silently `DuplicateKeyError`

The 1.2 spec lists only true and false as booleans. YAML 1.1 also accepted y, n, yes, no, on and off in several capitalizations.

Block scalars

Style Line breaks Final newline
Literal (vertical bar header) Each one is kept One, the default "clip"
Literal with `-` after the bar Each one is kept None, "strip"
Literal with `+` after the bar Each one is kept All trailing blank lines, "keep"
Folded (`>` header) Single breaks become spaces, blank lines become newlines One
Folded with `-` Same as folded None

The headers are written as key: |, key: |-, key: |+, key: > and key: >-.

Quoting

Style Escapes Use it for
Plain `text` None Simple words and numbers
Single `'text'` `''` is one quote Text with `: ` or `#` and no backslashes
Double `"text"` `\n`, `\t`, `\uXXXX`, `\"` Text that needs escapes

Special characters

Character Meaning Quote when value starts with it
`&name` Anchor Yes
`*name` Alias to an anchor Yes
`!tag` Tag or type Yes
`@` and backtick Reserved Yes
`: ` (colon, space) Key separator Quote any value containing it
` #` (space, hash) Starts a comment Quote any value containing it
`-` followed by space Sequence entry Yes
`? ` Complex key Yes

Common patterns

Write a typical config

import json
import yaml

doc = """
name: web
replicas: 3
ratio: 0.5
enabled: true
tags: [api, public]
env:
  - name: PORT
    value: "8080"
  - name: DEBUG
    value: "false"
limits: {cpu: 500m, memory: 256Mi}
notes: ~
"""
print(json.dumps(yaml.safe_load(doc)))
{"name": "web", "replicas": 3, "ratio": 0.5, "enabled": true, "tags": ["api", "public"], "env": [{"name": "PORT", "value": "8080"}, {"name": "DEBUG", "value": "false"}], "limits": {"cpu": "500m", "memory": "256Mi"}, "notes": null}

Quote "8080" and "false" when the consumer expects a string. Unquoted, they would be a number and a boolean.

Keep line breaks or fold them

import yaml

doc = """
keep: |
  line1
  line2
fold: >
  folded
  text

  new paragraph
strip: |-
  no trailing newline
"""
for k, v in yaml.safe_load(doc).items():
    print(k, repr(v))
keep 'line1\nline2\n'
fold 'folded text\nnew paragraph\n'
strip 'no trailing newline'

Use | for scripts and certificates, where line breaks matter. Use > for long prose. Add - when the value must not end with a newline, for example a token read from a file.

Reuse a block with anchors and merge keys

import json
import yaml

doc = """
defaults: &defaults
  adapter: postgres
  host: localhost
development:
  <<: *defaults
  database: dev
production:
  <<: *defaults
  host: db.example.com
  database: prod
"""
print(json.dumps(yaml.safe_load(doc)))
{"defaults": {"adapter": "postgres", "host": "localhost"}, "development": {"adapter": "postgres", "host": "localhost", "database": "dev"}, "production": {"adapter": "postgres", "host": "db.example.com", "database": "prod"}}

&defaults names a node and *defaults repeats it. The << merge key copies a mapping's pairs, and keys written beside it override the copy, as host does in production. The merge key comes from the YAML 1.1 type repository and is not part of the 1.2 core schema, so check that your parser supports it.

Put several documents in one file

import yaml

for d in yaml.safe_load_all("name: a\n---\nname: b\n...\n---\n- c\n"):
    print(d)
{'name': 'a'}
{'name': 'b'}
['c']

A --- line starts a document and ... ends one. Use safe_load_all to read every document, because safe_load raises an error when a file holds more than one.

Quote values that look like syntax

import yaml

tests = ['title: Note: draft', 'title: "Note: draft"', 'tag: #hash', 'tag: "#hash"', 'msg: @user', 'path: *.log', 'path: "*.log"', 'v: yes', 'v: "yes"']
for t in tests:
    try:
        print(t.ljust(22), yaml.safe_load(t))
    except yaml.YAMLError as e:
        print(t.ljust(22), 'ERROR', str(e).splitlines()[0])
title: Note: draft     ERROR mapping values are not allowed here
title: "Note: draft"   {'title': 'Note: draft'}
tag: #hash             {'tag': None}
tag: "#hash"           {'tag': '#hash'}
msg: @user             ERROR while scanning for the next token
path: *.log            ERROR while scanning an alias
path: "*.log"          {'path': '*.log'}
v: yes                 {'v': True}
v: "yes"               {'v': 'yes'}

Note that tag: #hash does not fail. The # starts a comment, so the value is silently empty. When in doubt, use double quotes.

Make a parser reject duplicate keys

from ruamel.yaml import YAML

try:
    YAML(typ="safe").load("a: 1\na: 2\n")
except Exception as e:
    print(type(e).__name__, str(e).splitlines()[0])
DuplicateKeyError while constructing a mapping

The spec requires mapping keys to be unique, but PyYAML's safe_load returns {'a': 2} for the same input. A duplicate key in a long file is a classic way to lose a setting without any warning, so lint YAML in CI.

Pitfalls

  • The Norway problem: country: NO loads as False in a 1.1 parser, so a list of country codes breaks at Norway. Quote it as "NO" or use a 1.2 parser.
  • Leading zero means octal in 1.1: mode: 0755 becomes 493 in PyYAML but 755 in ruamel.yaml. Write "0755" for a string, or 0o755 where the parser supports 1.2 integers.
  • Versions lose a digit: version: 1.10 becomes the float 1.1 in both parsers. Quote every version number, as in "1.10". A ZIP code such as zip: 02134 is even worse, because PyYAML reads it as octal and returns 1116.
  • Tabs in indentation: a: then a tab-indented - x fails with found character '\t' that cannot start any token. Configure the editor to insert spaces for .yml and .yaml files.
  • Colon plus space in a value: title: Note: draft fails with mapping values are not allowed here. Quote the value.
  • Unquoted # is a comment: tag: #hash silently yields null, and v: a #comment keeps only a. A # inside a word, as in a#b, is fine.
  • Duplicate keys overwrite silently: PyYAML keeps the last of two a: keys. Use a linter or a parser that errors on duplicates.
  • Wrong indentation under a list: a - y indented one space less than its sibling - x fails with while parsing a block mapping. Keep every item of a list at the same column.
  • Merge keys are not in the 1.2 spec: <<: *defaults works in both PyYAML and ruamel.yaml here, but it comes from YAML 1.1, so another parser may keep << as a literal key. Test your tool before relying on it.

Related ZipKit tools

Related cheatsheets