YAML Cheatsheet - Syntax, Anchors, Lists & Examples

Across Docker Compose, Kubernetes and GitHub Actions, the most common mistakes are wrong indentation, a missing space after the colon, and unquoted special characters. This reference arranges the 8 most-used syntax groups (basics, data types, lists and dicts, multiline strings, anchors and aliases, type tags) by purpose: mirror the scaffolding when writing config, and debug indentation or quoting section by section when an error appears. It also notes where YAML differs from JSON — single quotes do not escape, comments are allowed, and anchors cut repetition. After reading you can write clean, maintainable config files on your own.

Config Formats·54 commands·Last updated 2026-07-21

Typical Use Case

For developers writing or maintaining configuration: Docker Compose, Kubernetes manifests, GitHub Actions workflows, CI pipelines and front-end build tool configs. The traps that cost the most time get the most space — a space is required after the colon or the value is parsed as a string, indentation must be spaces and never tabs, and the block scalars pipe and folded differ in how they chomp newlines. Anchors with the merge key remove duplicated blocks entirely, which is the clean way to share one environment across several Compose services or one variable set across CI jobs. The explicit type tags exist to solve the classic problem of every environment variable arriving as a string.

Basic Syntax 8

key: value
Key-value pair (space required after colon)
# comment
Single-line comment
---
Document separator (for multi-doc files)
...
Document end marker
string
String (quotes not needed by default)
"string" / 'string'
Double quotes (escapes) / single quotes (no escapes)
key: "value: with colon"
Quote values with special chars (: # etc.)
key with spaces: value
Key names may contain spaces (no quotes needed)

Data Types 8

null / ~
Null / ~
true / false
Boolean
123 / 0x1A / 0o17 / 0b1010
Integer (decimal/hex/octal/binary)
3.14 / .inf / -.inf / .nan
Float (inf/-inf/nan)
2026-07-19
Date (ISO 8601)
2026-07-19T10:30:00Z
Datetime (UTC)
2026-07-19T10:30:00+08:00
Datetime with timezone
!!binary "aGVsbG8="
Binary data (base64)

List / Array 6

- item1 - item2
Block-style list
[item1, item2]
Flow-style list (inline)
fruits: - apple - banana
Nested list
items: []
Empty list
matrix: - [1, 2] - [3, 4]
List of lists
users: - name: tom age: 20
List of dicts

Dictionary / Object 6

key1: value1 key2: value2
Block-style dict
{key1: value1, key2: value2}
Flow-style dict (inline)
person: name: tom age: 20
Nested dict
person: {name: tom, age: 20}
Flow-style nested dict
config: {}
Empty dict
server: host: localhost ports: - 80 - 443
Dict and list mixed nesting

Multiline String 7

text: | line1 line2
Keep newlines (Literal block)
text: > line1 line2
Fold newlines into spaces (Folded block)
text: "line1\nline2"
Explicit newline inside double quotes
text: |- line1 line2
Keep newlines, drop trailing newline (- chomp)
text: |+ line1 line2
Keep newlines, keep trailing blank (+ chomp)
text: |2 line1 line2
Explicit indentation indicator (2 spaces)
text: >- line1 line2
Fold newlines and drop trailing newline

Anchors & Aliases 6

default: &default key: value
Define an anchor (&)
env: *default
Reference an anchor (*)
env: <<: *default extra: value
Merge an anchor into the current dict (<<)
list: &list [a, b, c]
Define an anchor for a list
copy: *list
Reference a list anchor
config: <<: [*base, *override]
Merge multiple anchors (later overrides earlier)

Type Conversion 8

key: !!str 123
Cast integer to string "123"
key: !!int "42"
Cast string to integer 42
key: !!float "3.14"
Cast string to float
key: !!bool "yes"
Cast string to boolean
key: !!timestamp "2026-07-19"
Cast string to timestamp
key: !!null ""
Cast empty string to null
key: !!seq {}
Explicitly declare a sequence type
key: !!map []
Explicitly declare a mapping type

Real-world Examples 5

services: web: image: nginx:latest ports: - "80:80"
Docker Compose service definition
steps: - name: Checkout uses: actions/checkout@v4
GitHub Actions step
apiVersion: apps/v1 kind: Deployment metadata: name: my-app
Kubernetes resource definition
--- title: My Post date: 2026-07-19 ---
Front Matter (post metadata)
env: DATABASE_URL: postgres://localhost/mydb
Environment variable config

Parameter matrix

ParameterEffectExample
key: valueBasic key-value form; a single space is required after the colonname: my-app
"string" / 'string'Double quotes support escapes; single quotes do notmsg: "a\tb"
# commentSingle-line comment, from # to the end of the line# production port
--- / ...Document separator and document end marker, for multiple documents---
null / ~Explicit null; the reader will not raise a type errordebug: null
0x1A / 0o17 / 0b1010Hexadecimal / octal / binary integer formsmask: 0b1010
!!str 123 / !!int "42"Type coercion, resolving the "everything becomes a string" pitfallport: !!int "8080"
!!timestamp "..."Parse a string as the timestamp typeat: !!timestamp "2026-07-19"
| and >| preserves newlines, > folds newlines into spacestext: >
|- / >- / |+chomp control: strip the trailing newline / keep trailing blank linestext: |-
&name to define / *name to referenceAnchor and alias, reducing repeated literalsenv: *default
<<: *nameMerge key, merging the anchor mapping into the current section<<: *default
<<: [*a, *b]Merge multiple anchors, the later overriding the earlier<<: [*base, *override]
- item / [a, b]Block-style and flow-style listsports: - 80
{} and []Explicit empty mapping and empty list, avoiding reading nullconfig: {}

Common pitfalls

SymptomThe parser treats the whole file as a single string, and no key can be read

CauseNo space after the colon. YAML requires key: value; key:value is parsed as a plain scalar string.

FixChange every key:value to key: value, paying special attention to URL fragments and line-continuation points.

SymptomIt reports "found character that cannot start any token" or an indentation error

CauseYAML is indentation-sensitive and does not allow Tab indentation; items at the same level must be strictly aligned.

FixReplace all Tabs with equal-width spaces, and enable whitespace display in the editor to check the levels.

SymptomA long passage is joined into one line and the backslash line continuation stops working

CauseBackslash line continuation only applies to flow-style (inline) scalars; indented block-style lines keep the newline when continued.

FixUse text: > to fold into one line, and text: | to preserve newlines as-is.

SymptomAfter merging with <<, a later same-named key is overwritten by the old value from the anchor

CauseThe merge key means "merge the values from the anchor in"; a same-named key written explicitly in this section is the local key, and a same-named key written in the anchor does not participate in overriding.

FixTo override, write the key directly in the current section (no need to write it into the merged anchor); for multiple anchors use <<: [*base, *override] with override last.

SymptomThe environment variable PORT=8080 is read as a string and port validation fails

CauseYAML does not infer types automatically; externally supplied values are parsed as strings by default.

FixUse an explicit type tag: port: !!int "${PORT}".

SymptomContent containing : or # inside a value is truncated and the remainder disappears

CauseA colon followed by a space, or a hash preceded by a space, is treated as a key-value separator or a comment start.

FixWrap the whole value in double or single quotes; use double quotes when escapes must be preserved.

SymptomAn extra blank line appears at the end, and file comparisons come out inconsistent

CauseBlock scalars keep the trailing newline by default; an explicit indentation indicator like |2 infers content indentation of 2 spaces, and without it the first content line governs.

FixUse |- to strip the trailing newline and |+ to explicitly keep trailing blank lines; standardize on one style across the team.

SymptomThe same config yields different types on two parsers (one int, one str)

CauseYAML 1.1 vs 1.2 spec differences: 1.1 treats yes/no/on/off as booleans and leading-zero numbers as octal, and some parsers still follow 1.1.

FixWrite booleans uniformly as true / false, and add explicit quotes when the string "yes" is intended.

Troubleshooting

  1. 1Confirm the real result after anchors and merge keys are expanded

    cat config.yaml

    Anchors and merge keys are pure syntactic sugar; what lands is the expanded literal. When the expansion is ambiguous, print the file first and check line by line.

  2. 2Validate with Python that the YAML parses and inspect the resulting types

    python -c "import yaml,pprint; pprint.pprint(yaml.safe_load(open('config.yaml', encoding='utf-8')))"

    If it prints a Python dict/list the syntax is correct; on a ScannerError the line and column in the message locate the problem directly.

  3. 3Check whether Tab indentation is mixed in

    grep -Pn '\t' config.yaml

    Any output means Tab indentation is present and must all be replaced with spaces.

  4. 4Troubleshoot a missing space after a colon

    grep -nE '^[ ]*[A-Za-z0-9_.-]+:[^ ]' config.yaml

    The matched lines are key-value pairs suspected of a missing space; confirm one by one whether a space is needed.

  5. 5Validate that each document in a multi-document file is independently valid

    python -c "import yaml; print(len(list(yaml.safe_load_all(open('config.yaml', encoding='utf-8')))))"

    The number of documents output should match the number of --- separators in the file; a difference of 1 means a separator is misplaced.

Command Examples

Reuse a common block with anchor merge

defaults: &defaults replicas: 2 image: nginx:1.25 env: production app: <<: *defaults name: web

&defaults defines the anchor and <<: *defaults merges the shared fields into app; the explicitly written name: web overrides the same-named key from the anchor. When many services reuse shared config, this avoids copying each block.

Output

After parsing, equivalent to:
app:
  name: web
  replicas: 2
  image: nginx:1.25
  env: production

Quote port mappings in Docker Compose

services: web: image: nginx:latest ports: - "8080:80" environment: NGINX_PORT: "80"

The "8080:80" in ports must be double-quoted, otherwise the colon followed by a number is parsed as sequence syntax and errors; values under environment should also be quoted when the string semantics must be preserved.

Common Pitfalls

  • Indentation must stay consistent — never mix space counts or use Tab within the same level, or the parser errors or nests deeper than intended.
  • A space is required after the colon (key: value); otherwise the whole line is treated as a string and the config silently does nothing.
  • Quote values that begin with special chars such as : # @, or they are parsed as tags, comments or anchor syntax.
  • Dates and numbers are auto-typed (2026-07-19 parses as a Date); add quotes when you want string semantics.
  • The << merge is overridden by explicit keys: an explicit definition always wins, so do not rely on an anchor to overwrite a written value.

Tips

  • YAML is indentation-sensitive: use spaces, not Tab, and align the same level.
  • A space is required after the colon (key: value), or it parses as a string.
  • Use | to keep newlines and > to fold them (good for long paragraphs).
  • Quote strings containing special chars (: # @ ` etc.) to avoid parse ambiguity.
  • Anchors & define, aliases * reference, << merges - reduces repeated config.
  • Multi-document files use --- separators, common in Front Matter and CI/CD.

FAQ

Why does dropping the space after the colon turn my key into a plain string?

YAML only treats a colon followed by a space as the key/value separator. Writing url:localhost:3306 makes the whole line one scalar string, so map lookups find no key. Add the space (url: localhost:3306), or quote the value when it contains colons: key: "value: with colon". The same applies to indentation: tabs are illegal, use spaces and keep the level aligned.

Should I use a literal pipe or a folded angle bracket for multi-line text?

It depends on whether you want the line breaks. text: | is the literal scalar: every newline is kept as-is, which is what you want for code snippets and config dumps. text: > is the folded scalar: consecutive line breaks collapse into a single space, ideal for prose paragraphs. Both accept chomping modifiers — |2 sets the explicit indentation indicator, |- strips the trailing newline, >- folds and strips it. For byte-exact control use text: "line1\nline2".

When are anchors, aliases and the merge key actually worth using?

Use them whenever the same default block is being repeated across many places. Define the anchor once with default: &default, reference it elsewhere with env: *default, and when you want a base plus local overrides, use the merge key: env:\n <<: *default\n extra: value. Several anchors can merge at once with config:\n <<: [*base, *override], where later entries win. Lists work too: anchor them with list: &list [a, b, c] and pull them back with copy: *list.

My numbers and booleans keep coming back as strings — how do I force the type?

Prefix the scalar with an explicit tag. key: !!str 123 turns the integer 123 into a string, and the reverse works too. The usual set is !!int "42", !!float "3.14", !!bool "yes", !!timestamp "2026-07-19" and !!null "" for turning an empty string into null. You can also pin the container shape with !!seq {} and !!map [] so downstream parsers never guess. A plain YAML file otherwise infers types, which is exactly where surprises come from.

How do I keep several configs inside one file with the --- separator?

--- starts a new document, splitting the file into independent units that each have their own top-level structure; a parser receives a list of documents. The most common case is front matter: open with ---, put title: My Post and date: 2026-07-19 inside, close with another ---, and the body becomes the second document. ... marks an explicit document end. You will see this pattern constantly in CI/CD files and multi-resource Kubernetes manifests.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us