YAML: The Config File Format You Can't Escape
Docker Compose, GitHub Actions, Kubernetes, Ansible, CI/CD pipelines — they all use YAML. It's designed to be human-readable, but its whitespace sensitivity and implicit typing cause endless debugging sessions.
Basic Syntax
# Key-value pairs
name: John Doe
age: 30
email: john@example.com
active: true
# Nested objects (indentation = structure)
database:
host: localhost
port: 5432
name: myapp
credentials:
username: admin
password: secretLists (Arrays)
# Block style
fruits:
- apple
- banana
- cherry
# Inline style
fruits: [apple, banana, cherry]
# List of objects
users:
- name: Alice
role: admin
- name: Bob
role: userData Types
# Strings (quotes optional for simple strings)
name: John # string
quoted: "Hello, World" # explicit string
single: 'raw: no escaping' # literal string (no escape sequences)
# Numbers
count: 42 # integer
price: 19.99 # float
hex: 0xFF # hexadecimal
octal: 0o77 # octal
# Booleans
active: true # also: True, TRUE, yes, Yes, on
inactive: false # also: False, FALSE, no, No, off
# Null
value: null # also: ~, Null, NULL
# Dates
created: 2026-04-23 # ISO 8601 date
timestamp: 2026-04-23T10:30:00ZMultiline Strings
# Literal block (preserves newlines) — use |
description: |
This is line 1.
This is line 2.
This is line 4 (blank line preserved).
# Folded block (newlines become spaces) — use >
summary: >
This is a long paragraph
that will be folded into
a single line.
# With chomp indicators
keep_trailing: |+ # keep trailing newlines
strip_trailing: |- # strip trailing newlinesYAML Gotchas (The Dangerous Parts)
# GOTCHA 1: Norway problem
country: NO # Parsed as boolean false!
country: "NO" # String — always quote these
# GOTCHA 2: Version numbers
version: 3.10 # Parsed as float 3.1 !
version: "3.10" # String — always quote versions
# GOTCHA 3: Timestamps
value: 2024-01-01 # Parsed as Date, not string!
value: "2024-01-01" # String
# GOTCHA 4: Indentation must be spaces, never tabs
# Tabs will cause parse errors
# GOTCHA 5: Colon in values
message: error: not found # Parse error!
message: "error: not found" # Quoted — worksAnchors and Aliases (DRY)
# Define anchor with &
defaults: &defaults
adapter: postgres
host: localhost
port: 5432
# Use alias with *
development:
<<: *defaults # merge defaults
database: myapp_dev
production:
<<: *defaults # merge defaults
host: prod-db.example.com
database: myapp_prodEnvironment Variables
# Docker Compose style
services:
app:
environment:
- DATABASE_URL=${DATABASE_URL}
- NODE_ENV=productionValidating YAML
# Python
import yaml
with open('config.yml') as f:
config = yaml.safe_load(f) # safe_load, not load!
# Node.js
import { parse } from 'yaml';
const config = parse(fs.readFileSync('config.yml', 'utf8'));
# CLI
yamllint config.yml # pip install yamllintValidate your YAML: YAML Formatter — paste YAML, validate syntax, convert to JSON.