YAML (“YAML Ain’t Markup Language”) is a human-readable data format used heavily in config files, Docker Compose, Kubernetes, Ansible, CI/CD pipelines, and more.
Mental model: YAML is JSON without the brackets and quotes — identical data structure, friendlier syntax.
Core Rules
- Indentation = structure (use spaces only, never tabs)
- Case-sensitive
#= comment- No quotes needed for simple strings (but they’re allowed)
Data Types
| YAML | JSON | |
|---|---|---|
| String | name: my-service | "name": "my-service" |
| Quoted string | quoted: "hello world" | "quoted": "hello world" |
| Number | port: 8080 | "port": 8080 |
| Float | ratio: 0.75 | "ratio": 0.75 |
| Boolean | enabled: true | "enabled": true |
| Null | value: null or value: ~ | "value": null |
Multiline strings (YAML only — no JSON equivalent)
# Literal block — preserves newlines
message: |
line one
line two
# Folded block — newlines become spaces
description: >
this becomes
one long line// JSON has no multiline syntax — you escape manually
{ "message": "line one\nline two" }Collections
Mapping (object)
server:
host: localhost
port: 3000
ssl: true{
"server": {
"host": "localhost",
"port": 3000,
"ssl": true
}
}Sequence (array)
ports:
- 80
- 443
- 8080
# Inline style (also valid)
tags: [web, api, prod]{
"ports": [80, 443, 8080],
"tags": ["web", "api", "prod"]
}Nested
services:
web:
image: nginx
ports:
- "80:80"
db:
image: postgres
environment:
POSTGRES_DB: myapp
POSTGRES_USER: admin{
"services": {
"web": {
"image": "nginx",
"ports": ["80:80"]
},
"db": {
"image": "postgres",
"environment": {
"POSTGRES_DB": "myapp",
"POSTGRES_USER": "admin"
}
}
}
}Anchors & Aliases (DRY reuse — YAML only)
JSON has no reuse mechanism. In YAML, define an anchor with &name and merge it with <<: *name.
defaults: &defaults # define anchor
restart: always
logging:
driver: json-file
web:
<<: *defaults # merge anchor
image: nginx
api:
<<: *defaults # reuse again
image: node:20{
"web": {
"restart": "always",
"logging": { "driver": "json-file" },
"image": "nginx"
},
"api": {
"restart": "always",
"logging": { "driver": "json-file" },
"image": "node:20"
}
}(JSON equivalent requires full repetition — no shorthand)
Environment Variables (Compose / K8s)
# As a list
environment:
- NODE_ENV=production
- PORT=3000
# As a map (preferred)
environment:
NODE_ENV: production
PORT: 3000{
"environment": ["NODE_ENV=production", "PORT=3000"]
}
// or
{
"environment": {
"NODE_ENV": "production",
"PORT": "3000"
}
}Real-World Patterns
Docker Compose
version: "3.9"
services:
app:
build: .
ports:
- "3000:3000"
depends_on:
- db
environment:
DATABASE_URL: postgres://admin:secret@db:5432/myapp
db:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:{
"version": "3.9",
"services": {
"app": {
"build": ".",
"ports": ["3000:3000"],
"depends_on": ["db"],
"environment": {
"DATABASE_URL": "postgres://admin:secret@db:5432/myapp"
}
},
"db": {
"image": "postgres:15",
"volumes": ["pgdata:/var/lib/postgresql/data"]
}
},
"volumes": { "pgdata": {} }
}GitHub Actions
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test{
"on": {
"push": { "branches": ["main"] }
},
"jobs": {
"test": {
"runs-on": "ubuntu-latest",
"steps": [
{ "uses": "actions/checkout@v4" },
{ "run": "npm ci" },
{ "run": "npm test" }
]
}
}
}Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:latest
ports:
- containerPort: 3000{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": { "name": "my-app" },
"spec": {
"replicas": 3,
"selector": { "matchLabels": { "app": "my-app" } },
"template": {
"metadata": { "labels": { "app": "my-app" } },
"spec": {
"containers": [
{
"name": "my-app",
"image": "my-app:latest",
"ports": [{ "containerPort": 3000 }]
}
]
}
}
}
}Common Gotchas
| Problem | Fix |
|---|---|
| Tabs instead of spaces | Always use spaces |
yes/no parsed as booleans | Quote them: "yes" |
| Colon in a value | Quote the string: "http://example.com" |
Leading zeros (08) parsed as octal | Quote it: "08" |
| Inconsistent indentation | Pick 2 or 4 spaces and stick to it |