Comprehensive reference for yaml syntax reference covering essential concepts, practical examples, and production best practices.
YAML (YAML Ain’t Markup Language) is a data serialization format designed for human readability and machine parseability. It appears wherever configuration, manifests, or pipeline definitions are authored outside of a programming language: Kubernetes resource files, Ansible playbooks, CI/CD pipeline definitions, Docker Compose files, and internal tooling configs. This reference covers the syntax itself—not tool-specific workflows, not language bindings, and not the semantics of any particular consumer. If you are editing a YAML file and seeing parse errors, unexpected type coercion, or indentation-related failures, this is the reference you open.
A scalar is a single value: string, integer, float, boolean, or null. YAML distinguishes these by context and quoting, but the parser’s default behaviour is strict.
Unquoted string
service_name: api-gateway
Integer
replica_count: 3
Float
timeout_seconds: 4.5
Boolean – YAML 1.1 and 1.2 accept several plain‑text equivalents, but they are not interchangeable with the strings "true" or "false".
enabled: true
Null – three spellings are accepted; all resolve to null in the constructed object.
parent: ~
or
parent: null
or
parent: ""
(An unquoted empty value is *not* null; it is a missing key.)
String that looks like a boolean or number – always quote if you intend it as text.
status: "true"
version: "3.2"
A mapping is a set of key–value pairs. A sequence is an ordered list. Most YAML documents combine both.
Mapping
database:
host: localhost
port: 5432
ssl: false
Sequence (block style)
environments:
- development
- staging
- production
Sequence (flow style)
features: [auth, logging, metrics]
Nested mapping inside a sequence
services:
- name: web
port: 8080
- name: api
port: 3000
Key ordering is not guaranteed by the spec, but most parsers preserve insertion order when using safe_load or safe_dump. If order matters for downstream consumers, verify the specific tool’s behaviour.
Anchors allow a node to be defined once and referenced elsewhere. Aliases pull in the anchored node. Tags override or specify a type.
Anchor and alias
&db_default
database:
host: localhost
port: 5432
development:
<<: *db_default
name: dev_db
production:
<<: *db_default
name: prod_db
Here <<: merges the anchored mapping into the current context. The * syntax pulls the alias forward. If two anchors define the same key, the closer definition wins.
Tag override
!!str "hello world"
The !!str tag forces the scalar to be interpreted as a string, even if the parser would otherwise coerce it. In YAML 1.1, !!str is the default for values that look like strings; in YAML 1.2, all scalars default to string unless another tag matches.
Custom tags can be defined in a schema, but they are rarely needed in general-purpose config. If you see !!python/object/apply:... in a file you did not write, it indicates a deserialization risk.
YAML is sensitive to leading whitespace. Tabs are discouraged; the spec says they should be expanded as 2 spaces, but many parsers diverge.
*Exact failure*: A file containing a single tab character for indentation under apiVersion: in a Kubernetes manifest produces:
error: mapping values are not allowed here
The parser has consumed the previous key’s value and hit the tab before the next key:, which it interprets as a new mapping entry in an invalid position.
Rule: Use 2-space indentation. Never mix tabs and spaces. Most linters (yamllint, prettier with YAML plugin) will flag this immediately.
YAML’s default type coercion is the most common source of production bugs.
flag: yes
yes coerces to true. Similarly no → false, on → true, off → false. If a downstream service expects the string "yes" (e.g., as a feature flag passed to an environment variable), the coercion breaks the flow.
*Exact result*: Running python3 -c "import yaml; print(yaml.safe_load('flag: yes'))" returns {'flag': True}. Running the same with flag: "yes" returns {'flag': 'yes'}.
Never rely on implicit coercion for user‑controlled or externally sourced YAML. Always quote values that should remain strings.
Flow style ({} and []) does not support multi‑line nesting. Attempting to mix flow and block styles in the same mapping often produces a parse error that is not obvious.
# ❌ Invalid – flow style inside a block mapping
services:
{ name: web, port: 8080 }
Most parsers reject this outright:
yaml.parser.ParserProblem: while parsing a block mapping
in "<string>", line 2, column 5
could not find expected ':'"
If you need flow style, keep the mapping entirely in flow syntax, or entirely in block syntax.
The YAML spec says behaviour on duplicate keys is undefined. parsers typically overwrite the first value with the last, but this is implementation‑dependent.
x: 1
x: 2
python3 -c "import yaml; print(yaml.safe_load('x: 1\nx: 2'))" returns {'x': 2}. A Go YAML library might raise an error or return the first value. Never depend on a specific behaviour; deduplicate keys before parsing if uniqueness is required.
Concrete commands for the most common workflows.
Python (stdlib yaml)
python3 -c "import yaml, sys; print(yaml.safe_load(sys.stdin))" < config.yaml
If the file contains a syntax error, the command aborts with:
yaml.scanner.ScannerError: mapping values are not allowed here
in "<string>", line 1, column 1
Python – strict mode, dump + load round‑trip
python3 -c "
import yaml, json, sys
data = yaml.safe_load(open('config.yaml'))
print(json.dumps(data, indent=2))
" 2>&1 | head -20
This catches type‑coercion issues that survive a safe‑load, because json.dumps will fail on non‑JSON‑compatible types (e.g., Python datetime objects, which yaml.safe_load can produce if a !!timestamp tag is present).
yq (Go-based, commonly used in shells)
yq eval '.' config.yaml
If the file is valid YAML, yq prints the parsed structure to stdout. If there is a syntax error:
error: parse error: while parsing indicator
yq does not attempt type coercion; it either parses or fails.
JSON‑YAML conversion sanity check
python3 -c "
import yaml, json
yaml_str = open('config.yaml').read()
data = yaml.safe_load(yaml_str)
print(json.loads(json.dumps(data)))
"
If this succeeds, the YAML is both valid and round‑trip safe through JSON (i.e., no Python‑specific types leaked in).
The two versions ship with different default tags and type‑coercion rules. Most modern tools default to 1.2, but legacy configs may declare yaml-1.1 via a document header.
YAML 1.1 defaults
!!str is implicit for plain scalarsyes/no/on/off are booleans!!timestamp parses date‑time strings into datetime objectsYAML 1.2 defaults
str by defaultyes/no/on/off are *not* booleans unless explicitly tagged!!timestamp is not a default tag; date‑time strings remain strings unless !!timestamp is usedHow to declare the version (rarely needed, but concrete):
%YAML 1.1
---
or
%YAML 1.2
---
If your tooling is mixing versions (e.g., a Kubernetes cluster that parses 1.1 but your CI pipeline validates 1.2), you may see different type coercion on the same key. Pin the version at the top of the file if you must control behaviour, and document which version your consumers expect.
| Construct | Syntax | Notes |
|---|---|---|
| String (unquoted) | key: value | No quotes needed if no special chars |
| String (quoted) | key: "value" or key: 'value' | Use when value looks like boolean/number |
| Integer | key: 42 | |
| Float | `key: |
This page was rewritten on 10 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy