YAML Cheatsheet
Quoting
Use this YAML reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
The decision rule
Three ways to write a string. Pick with this order, top to bottom, first match wins.
1. The value must contain \n, \t, or a \uXXXX escape → double quotes.
2. The value could be read as a number, bool, null, date, or version, or it starts
with an indicator character, or it contains : or # → single quotes.
3. Otherwise → plain, no quotes.
plain: api-gateway needs_single: '*' needs_single_too: 'no' needs_double: "line one\nline two"
Plain scalars, and what breaks them
A plain scalar is unquoted. It may contain most characters, but a handful of positions are reserved. These all fail or mis-parse:
| Input | Result |
|---|---|
k: a: b | error, "mapping values are not allowed here" |
k: a: | error, trailing : looks like a nested key |
k: a # c | "a", the # starts a comment |
k: - a | error, - at the start opens a sequence |
k: *a | error, * is an alias reference |
k: &a | null, & declared an anchor with no value |
k: !a | error, !a is an unknown tag |
k: ? a | error, ? is a complex-key indicator |
k: {a | error, { opens flow style |
k: %a | error, % is the directive indicator |
` k: a `` | error, backtick is reserved |
k: @a | error, @ is reserved |
Two cases that surprise people by working:
url: http://x.com # ok, ':' with no space after is fine hashtag: a#c # ok, '#' with no space before is fine negative: -a # ok, '-' only opens a sequence when followed by a space question: ?a # ok, same rule for '?'
So the real triggers are : (colon then space), # (space then hash), and an
indicator in first position. Everything else is safe plain.
Single quotes
Inside '...' there is exactly one escape: '' for a literal apostrophe.
Backslashes are literal characters.
a: 'it''s fine' # -> it's fine b: 'C:\Users\new' # -> C:\Users\new (no \n here) c: 'a #b' # -> a #b, the hash is inert d: '*' # -> * e: 'yes' # -> the string "yes"
Single quotes are the right default for anything you want taken literally:
Windows paths, regexes, glob patterns, Jinja and Helm templates, anything with a
{{ }} in it.
Double quotes
"..." is the only form with real escapes. This is the full table.
| Escape | Meaning |
|---|---|
\0 | null byte |
\a \b | bell · backspace |
\t | tab |
\n | line feed |
\v \f \r | vertical tab · form feed · carriage return |
\e | escape (0x1B) |
\" | double quote |
\/ | forward slash (for JSON compatibility) |
\\ | backslash |
\N \_ | next line (U+0085) · non-breaking space |
\L \P | line separator · paragraph separator |
\xNN | 8-bit code point |
\uNNNN | 16-bit code point |
\UNNNNNNNN | 32-bit code point |
\ + newline | line continuation, joins without a space |
msg: "tab\there, newline\nthere, caf\u00e9, \x41 = A"parses to 'tab\there, newline\nthere, café, A = A', so the literal string is:
tab<TAB>here, newline there, café, A = A
An unknown escape is a hard error, not a passthrough. "a\qb" fails to
scan. This is the trap with regexes: "\d+" is invalid YAML, so write '\d+'.
Line continuation folds a long double-quoted value onto one line:
long: "the quick \ brown fox"
parses to 'the quick brown fox', one space, because the leading indentation of
the next line is stripped.
Keys follow the same rules
A key is just a scalar, so it needs quoting for the same reasons, and it hits the type resolver too.
"a b": 1 # keys with spaces work plain too, but quote for clarity "on": true # WITHOUT quotes, PyYAML makes this the key True "1": one # WITHOUT quotes this is the integer key 1 "null": x # WITHOUT quotes this is the null key "off": disabled # WITHOUT quotes, PyYAML makes this the key False
on: 1 under PyYAML parses to {True: 1}, which is why GitHub Actions workflows
read fine in CI (its parser is 1.2) and blow up in a local Python linter. Quote
on, off, no, yes, y, n, true, false, null, and any all-digit key.
Quick reference
| You want | Write |
|---|---|
| plain word | name: api |
| the string "no" | country: 'no' |
| a version | version: '1.10' |
| a file mode | mode: '0644' |
| a regex | pattern: '\d{4}' |
| a Windows path | path: 'C:\tmp\new' |
| a Helm template | image: '{{ .Values.image }}' |
| an embedded newline | body: "a\nb" |
a leading * or & or % | glob: '*.log' |
| a colon-space inside text | note: 'time: now' |
| an empty string | s: '' |
a literal # mid-word | tag: a#b (no quotes needed) |