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.
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# comment---...string"string" / 'string'key: "value: with colon"key with spaces: valueData Types 8
null / ~true / false123 / 0x1A / 0o17 / 0b10103.14 / .inf / -.inf / .nan2026-07-192026-07-19T10:30:00Z2026-07-19T10:30:00+08:00!!binary "aGVsbG8="List / Array 6
- item1
- item2[item1, item2]fruits:
- apple
- bananaitems: []matrix:
- [1, 2]
- [3, 4]users:
-
name: tom
age: 20Dictionary / Object 6
key1: value1
key2: value2{key1: value1, key2: value2}person:
name: tom
age: 20person: {name: tom, age: 20}config: {}server:
host: localhost
ports:
- 80
- 443Multiline String 7
text: |
line1
line2text: >
line1
line2text: "line1\nline2"text: |-
line1
line2text: |+
line1
line2text: |2
line1
line2text: >-
line1
line2Anchors & Aliases 6
default: &default
key: valueenv: *defaultenv:
<<: *default
extra: valuelist: &list [a, b, c]copy: *listconfig:
<<: [*base, *override]Type Conversion 8
key: !!str 123key: !!int "42"key: !!float "3.14"key: !!bool "yes"key: !!timestamp "2026-07-19"key: !!null ""key: !!seq {}key: !!map []Real-world Examples 5
services:
web:
image: nginx:latest
ports:
- "80:80"steps:
- name: Checkout
uses: actions/checkout@v4apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app---
title: My Post
date: 2026-07-19
---env:
DATABASE_URL: postgres://localhost/mydbParameter matrix
| Parameter | Effect | Example |
|---|---|---|
key: value | Basic key-value form; a single space is required after the colon | name: my-app |
"string" / 'string' | Double quotes support escapes; single quotes do not | msg: "a\tb" |
# comment | Single-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 error | debug: null |
0x1A / 0o17 / 0b1010 | Hexadecimal / octal / binary integer forms | mask: 0b1010 |
!!str 123 / !!int "42" | Type coercion, resolving the "everything becomes a string" pitfall | port: !!int "8080" |
!!timestamp "..." | Parse a string as the timestamp type | at: !!timestamp "2026-07-19" |
| and > | | preserves newlines, > folds newlines into spaces | text: > |
|- / >- / |+ | chomp control: strip the trailing newline / keep trailing blank lines | text: |- |
&name to define / *name to reference | Anchor and alias, reducing repeated literals | env: *default |
<<: *name | Merge 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 lists | ports:
- 80 |
{} and [] | Explicit empty mapping and empty list, avoiding reading null | config: {} |
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
1Confirm the real result after anchors and merge keys are expanded
cat config.yamlAnchors 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.
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.
3Check whether Tab indentation is mixed in
grep -Pn '\t' config.yamlAny output means Tab indentation is present and must all be replaced with spaces.
4Troubleshoot a missing space after a colon
grep -nE '^[ ]*[A-Za-z0-9_.-]+:[^ ]' config.yamlThe matched lines are key-value pairs suspected of a missing space; confirm one by one whether a space is needed.
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