What Is YAML?
YAML originally stood for Yet Another Markup Language, but was retroactively renamed YAML Ain't Markup Language to emphasise that it is a data serialisation language, not a document markup language. YAML is designed to be highly human-readable: the spec explicitly prioritises readability over speed of parsing or compactness.
YAML is defined by the YAML specification (yaml.org), currently at version 1.2 (2009). YAML 1.2 is a strict superset of JSON — every valid JSON document is a valid YAML document. YAML adds comments, complex data types, multiple documents per file, references/aliases, and more expressive syntax.
YAML is the dominant format for DevOps configuration files: Kubernetes manifests, Docker Compose files, GitHub Actions workflows, Ansible playbooks, Helm charts, and countless CI/CD pipeline definitions.
Core Syntax
YAML uses indentation (spaces only, no tabs) to represent structure. The indentation level defines nesting:
# This is a comment
name: Alice
age: 30
active: true
score: 98.6
nickname: ~ # null
address:
street: 123 Main St
city: Springfield
zip: "12345" # Quoted to prevent integer interpretation
tags:
- admin
- editor
- developer
servers:
- host: web01
port: 80
ssl: false
- host: web02
port: 443
ssl: true
Key differences from JSON:
- No quotes required for most strings (but allowed)
- Colons and spaces separate keys from values (not braces/brackets for maps)
- Dashes for list items
#comments supported everywhere~ornullfor null values- No trailing commas (no commas at all in block style)
YAML Data Types
YAML's type system is richer than JSON's:
Scalars:
true,false,yes,no,on,off→ boolean (YAML 1.1 only; YAML 1.2 restricts totrue/false)42→ integer3.14→ float1.5e10→ scientific notation float0xFF→ hexadecimal integer0o755→ octal integer2024-01-15→ date (ISO 8601)2024-01-15T10:30:00Z→ datetime (ISO 8601)null,~→ null!!binary |+ base64 data → binary
Important gotcha: YAML 1.1 (used by many older parsers including PyYAML by default) treats yes, no, on, off as booleans. This causes famous problems with country codes:
country: NO # Parsed as boolean false in YAML 1.1!
enabled: on # Parsed as boolean true in YAML 1.1!
Always quote these if you mean the string values.
Block vs. Flow Style
YAML supports two syntactic styles:
Block style (the readable, multi-line format):
fruits:
- apple
- banana
- cherry
person:
name: Alice
age: 30
Flow style (inline, JSON-compatible):
fruits: [apple, banana, cherry]
person: {name: Alice, age: 30}
Both styles can be mixed freely in the same document.
Multi-Line Strings
YAML has two multi-line string styles:
Literal block scalar (|): Preserves newlines exactly:
description: |
Line one of the description.
Line two continues here.
Line three ends the block.
Folded block scalar (>): Folds newlines into spaces (single newlines become spaces; blank lines become newlines):
summary: >
This is a long paragraph that
wraps across multiple lines but
will be folded into one line.
A blank line creates a paragraph break.
The trailing | or > can be followed by a - (chomp: strip trailing newlines) or + (keep trailing newlines). | keeps exactly one trailing newline by default.
Anchors and Aliases
YAML's most powerful feature for DRY (Don't Repeat Yourself) configuration is anchors and aliases:
defaults: &defaults # & defines an anchor named "defaults"
timeout: 30
retries: 3
log_level: info
development:
<<: *defaults # << is the "merge key"; *defaults dereferences the anchor
database: dev_db
debug: true
production:
<<: *defaults # Inherits timeout: 30, retries: 3, log_level: info
database: prod_db
timeout: 60 # Override: production needs longer timeout
debug: false
The << merge key expands all key-value pairs from the referenced mapping into the current mapping. Locally defined keys override merged ones.
Multiple Documents in One File
A YAML file can contain multiple documents separated by ---:
---
# Document 1
name: Alice
role: admin
---
# Document 2
name: Bob
role: editor
---
# Document 3
name: Carol
role: viewer
This is used extensively in Kubernetes, where a single YAML file can define multiple resources (Deployment, Service, ConfigMap) separated by ---.
YAML in DevOps: Real Examples
Docker Compose:
version: '3.9'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
GitHub Actions:
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm test
YAML vs. JSON vs. TOML
| Feature | YAML | JSON | TOML |
|---|---|---|---|
| Comments | Yes | No | Yes |
| Human readability | Excellent | Good | Very good |
| Multi-line strings | Yes ( | , >) | No (escaped \n) |
| Dates natively | Yes | No | Yes |
| Anchors/aliases | Yes | No | No |
| Multiple documents | Yes (---) | No | No |
| Spec complexity | Very high | Very low | Medium |
| Parsing difficulty | High | Very low | Low |
| Use case | Config, K8s, CI/CD | APIs, data exchange | Config files |
YAML Security Pitfalls
YAML's richness creates real security risks:
Arbitrary code execution: YAML parsers that support the full specification (including language-specific tags like !!python/object/apply) can execute arbitrary code. A malicious YAML file could instantiate objects and call methods during parsing:
# In PyYAML's unsafe load mode:
exploit: !!python/object/apply:os.system
- "rm -rf /"
Always use safe loading:
- Python:
yaml.safe_load()(notyaml.load()) - JavaScript: use a parser that doesn't support executable tags
- Go: gopkg.in/yaml.v3 is safe by default
Billion laughs attack: Anchor/alias abuse can create exponential expansion:
a: &a [lol, lol, lol, lol]
b: &b [*a, *a, *a, *a]
c: &c [*b, *b, *b, *b]
# After 9 levels: 4^9 = 262,144 strings from 5 lines
Limit alias expansion depth in parsers that accept untrusted input.
Converting YAML
YAML → JSON: Most YAML is valid JSON-representable data. Python: json.dumps(yaml.safe_load(f)). For multi-document YAML, each document becomes a separate JSON object.
YAML → TOML: Both support comments and dates natively. Complex YAML features (anchors, multi-document) don't have TOML equivalents.
JSON → YAML: Nearly lossless. Python: yaml.dump(json.load(f), default_flow_style=False).
Summary
YAML's extraordinary human-readability — enabled by significant whitespace, optional quoting, comments, and anchors — makes it the dominant format for DevOps configuration. Its YAML 1.2 superset-of-JSON property gives it mathematical elegance. Its costs are real: the specification is one of the most complex in common use, parsers have historically had major security vulnerabilities, and the whitespace-significant syntax is error-prone (invisible tabs, indentation level errors). For machine-generated data interchange, JSON is simpler and safer; for human-authored infrastructure configuration, YAML is the standard.
Related conversions
Frequent conversions across the catalogue: