YAML Cheatsheet
Scalars and Types
Use this YAML reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
There are no type declarations
A plain (unquoted) scalar gets its type from a regex resolver, and that
resolver changed between YAML 1.1 and YAML 1.2. This reference targets 1.2
core and flags 1.1 wherever they disagree, because most parsers in the wild are
a mix: PyYAML's safe_load is effectively 1.1, Go's yaml.v3 and ruamel.yaml
default to 1.2, Ruby Psych sits in between.
count: 3 # int ratio: 0.5 # float name: api # str ready: true # bool extra: # null
The resolution table
Every row below was parsed, not remembered. "1.1" is PyYAML 6.0 safe_load,
"1.2" is the 1.2 core schema.
| Plain scalar | YAML 1.2 core | YAML 1.1 (PyYAML) |
|---|---|---|
null · Null · NULL · ~ · empty | null | null |
true · True · TRUE | bool true | bool true |
yes · on · No · OFF | string | bool |
1 · +12 · -17 | int | int |
1_000 | 1000 | 1000 |
0x1F | 31 | 31 |
0b101 | 5 | 5 |
0o17 | 15 | string "0o17" |
017 | 17 | 15 (octal) |
08 | 8 | string "08" |
1.5 · -0.01 | float | float |
1e3 | 1000.0 | string "1e3" |
1.0e3 | 1000.0 | string "1.0e3" |
6.02e+23 | float | float |
.inf · .Inf · -.INF | ±infinity | ±infinity |
.nan · .NaN | NaN | NaN |
12:30 | string | 750 (sexagesimal) |
1.10 | 1.1 | 1.1 |
1.0.0 | string | string |
2024-01-15 | date (see below) | date |
Two rows there cause most real incidents. 017 means fifteen to a 1.1
parser and seventeen to a 1.2 parser. 0644 becomes 420, which is exactly
why file modes in Kubernetes and Ansible have to be quoted.
Null and empty
Four spellings mean null, and a missing value means null too. An empty quoted string is a string, not null.
a: # null b: null # null c: ~ # null d: Null # null e: "" # empty string, NOT null f: "null" # the four-character string "null"
Parsed: {a: None, b: None, c: None, d: None, e: '', f: 'null'}. If your code
does if value: both null and "" are falsy, so the distinction only bites when
you check is None or serialize back out.
Integers
| Form | Example | Value |
|---|---|---|
| decimal | 42 | 42 |
| signed | +42 · -42 | 42 · -42 |
| underscore separated | 1_000_000 | 1000000 |
| hex | 0x1F · 0xDEADBEEF | 31 · 3735928559 |
| octal (1.2) | 0o755 | 493 in 1.2, string in PyYAML |
| octal (1.1) | 0755 | 493 in 1.1, 755 in 1.2 |
| binary | 0b1101_0110 | 214 |
Underscores are permitted in 1.1 and, as a widely implemented extension, in 1.2 parsers too. Do not rely on them for a config another team's parser reads.
Floats and the special values
plain: 3.1415 good_exp: 6.626e-34 # float everywhere also_good: 1.0e+3 # float everywhere bad_exp: 1e3 # float in 1.2, STRING in PyYAML also_bad: 5e+22 # float in 1.2, STRING in PyYAML still_bad: 1.0e3 # float in 1.2, STRING in PyYAML positive_inf: .inf negative_inf: -.Inf not_a_number: .nan
PyYAML's float regex demands both a decimal point and an explicitly signed
exponent, so only the 1.0e+3 shape is portable. 1e3, 5e+22, and 1.0e3 all
come back as strings from safe_load while a 1.2 parser reads them as floats.
There is no Infinity or NaN spelling in YAML, only the dot forms, and the
letters are case insensitive (.inf, .Inf, .INF).
Timestamps
YAML 1.1 defines a timestamp type. The 1.2 core schema does not, so a
strict 1.2 parser leaves a date as a string while most real implementations
(PyYAML, ruamel, yaml.v3) still resolve it.
date: 2024-01-15 # date utc: 2024-01-15T10:30:00Z # datetime, UTC offset: 2024-01-15T10:30:00-07:00 # datetime with offset spaced: 2024-01-15 10:30:00 # datetime, naive version: "2024-01" # quoted, stays a string
Because the type is parser dependent, quote any date you want as text. A
2024-01-15 that arrives as a Python datetime.date instead of a str is a
common source of "not JSON serializable" crashes downstream.
Strings are the fallback
Anything the resolver does not recognise is a string, which is why most YAML looks untyped and works anyway. The failure mode is the reverse: a value you meant as text that happens to match a number, bool, or date pattern.
# all of these need quotes mode: "0644" # else 420 in 1.1 release: "1.10" # else the float 1.1 country: "NO" # else false in 1.1 duration: "12:30" # else 750 in 1.1 tag: "3.9" # a float otherwise, and 3.10 sorts wrong
Rule of thumb: quote every version, mode, ID, country code, and time. It costs two characters and removes a whole class of bug.