YAML Quick Reference
Syntax · Data Types · Collections · Anchors · Tags · Real-World Patterns
Introduction
YAML (YAML Ain't Markup Language) is a human-readable data serialization format. It is widely used for configuration files, CI/CD pipelines, Kubernetes manifests, Ansible playbooks, and Docker Compose.
Human-Readable
Uses indentation and minimal punctuation so files are easy to read and write without tooling.
Superset of JSON
Any valid JSON is valid YAML. YAML extends JSON with comments, anchors, and multiline strings.
Whitespace-Sensitive
Indentation defines structure. Only spaces are allowed — never tabs — and consistency is mandatory.
Language-Agnostic
Parsers exist for Python, JavaScript, Go, Java, Ruby, and more. Maps to native data structures.
Never use tabs YAML strictly forbids tab characters for indentation. Always use spaces — 2 spaces per level is the most common convention.
Basic Syntax Rules
YAML is built on a small set of rules: key-value pairs, indentation for nesting, and # for comments.
# This is a comment — ignored by the parser # Basic key: value pair name: Akash age: 27 active: true score: 98.5 nothing: null # or use ~ as shorthand for null # Keys can contain spaces if quoted "full name": Akash Chaubey 'home city': Lucknow
| Rule | Detail |
|---|---|
| Key-Value Separator | Colon followed by a space: key: value — the space is mandatory |
| Comments | Start with # — everything after is ignored on that line |
| Indentation | Spaces only. Consistent depth per level (2 or 4 spaces recommended) |
| Case Sensitive | Name and name are different keys |
| Null value | Omit the value, write null, or use ~ |
| Special Characters | Characters like : { } [ ] , & * # ? | - < > = ! % @ \ need quoting in strings |
A colon with no trailing space (e.g. http://example.com) is treated as a plain string, not a key-value separator. Always add a space after : in mappings.
Data Types
YAML auto-detects types from the value's format. Understanding implicit typing prevents surprises.
# ── Integers ── decimal: 42 negative: -17 octal: 0o17 # YAML 1.2: 0o prefix hex: 0xFF binary: 0b1010 # ── Floats ── pi: 3.14159 scientific: 1.2e+10 infinity: .inf not_a_number: .nan # ── Booleans ── is_active: true # also: True, TRUE is_deleted: false # also: False, FALSE # ── Null ── empty_a: null empty_b: ~ empty_c: # value omitted → null # ── Timestamps ── date: 2026-06-03 datetime: 2026-06-03T10:30:00Z
Boolean gotcha — In YAML 1.1, values like yes, no, on, off were parsed as booleans. YAML 1.2 removed this. Quote them if you mean literal strings: "yes", "no".
Strings & Quoting
Strings in YAML do not require quotes in most cases. Quotes are needed when a value contains special characters or could be misinterpreted as another type.
# ── Plain (unquoted) ── city: Lucknow greeting: Hello World # ── Single-quoted — literal, no escape sequences ── path: 'C:\Users\akash\documents' price: '$100' escaped_quote: 'It''s a trap' # double '' for a literal ' # ── Double-quoted — supports escape sequences ── newline: "Line one\nLine two" tab: "Col1\tCol2" unicode: "\u0041 is the letter A" ampersand: "YAML & JSON" # & needs quoting
| Style | Quotes | Escape Sequences | When to use |
|---|---|---|---|
| Plain | None | None | Simple values with no special characters |
| Single-quoted | '…' | None (literal backslash) | Paths, regex, strings with : or # |
| Double-quoted | "…" | \n \t \u \\ etc. | Strings needing escape sequences |
Multiline Strings
Two block scalar indicators control how newlines and trailing whitespace are handled in multiline values.
script: | echo "Hello" echo "World" exit 0
Newlines are preserved as \n. Ideal for shell scripts, GitHub Actions steps, SQL queries.
description: > This is a long description that wraps across multiple lines in the source file.
Single newlines become spaces. Result is one long string. Good for long descriptions.
# Default: keeps ONE trailing newline default: | Hello # Strip (-): removes ALL trailing newlines stripped: |- Hello # Keep (+): preserves ALL trailing newlines kept: |+ Hello # Same modifiers work with folded (>- and >+) folded_strip: >- This becomes one long string with no trailing newline.
| Indicator | Newlines | Trailing newlines |
|---|---|---|
| | | Preserved as \n | One trailing newline (default) |
| |- | Preserved as \n | Stripped — none |
| |+ | Preserved as \n | All preserved |
| > | Folded to spaces | One trailing newline (default) |
| >- | Folded to spaces | Stripped — none |
| >+ | Folded to spaces | All preserved |
Lists & Mappings
YAML has two collection types: sequences (lists/arrays) and mappings (dictionaries/objects). Both support block and flow styles.
# Block style — one item per line fruits: - apple - banana - mango # Flow style — JSON-like, on one line colors: [red, green, blue] # List of numbers ports: [80, 443, 8080] # Empty list tags: []
# Block style person: name: Akash age: 27 city: Lucknow # Flow style — JSON-like point: {x: 10, y: 20} # Empty mapping metadata: {}
Block style is more readable for humans; flow style is compact and useful for short inline collections. You can mix them — a block sequence can contain flow mappings.
Nested Structures
Combine sequences and mappings at any depth. Indentation defines the parent-child relationship.
users: - name: Alice role: admin permissions: [read, write, delete] - name: Bob role: viewer permissions: [read]
app: server: host: 0.0.0.0 port: 8080 tls: enabled: true cert: /etc/ssl/cert.pem key: /etc/ssl/key.pem database: host: localhost port: 5432 name: myapp_db
matrix: - [1, 2, 3] - [4, 5, 6] - [7, 8, 9] environments: - name: dev vars: DEBUG: true LOG_LEVEL: verbose - name: prod vars: DEBUG: false LOG_LEVEL: error
Inconsistent indentation is the most common YAML error. Each level must be indented by the same number of spaces as its siblings. A linter like yamllint catches these early.
Anchors & Aliases
Anchors (&name) mark a node for reuse. Aliases (*name) reference it elsewhere — eliminating copy-paste in large config files.
# & defines the anchor, * references it original: &my_anchor Hello World copy_a: *my_anchor # → "Hello World" copy_b: *my_anchor # → "Hello World"
# Repeated config across three services — without anchors service1: env: prod retries: 3 version: 4.8 service2: env: prod retries: 3 version: 4.8 # ── With anchors ── defaults: &svc_config env: prod retries: 3 version: 4.8 service1: *svc_config service2: *svc_config
Anchor must precede alias — An anchor (&name) must be defined before any alias (*name) that references it. Forward references are not allowed.
A plain alias (*name) creates a complete copy of the anchored node. Modifications to the alias do not affect the original. Use merge keys (<<:) if you need to extend with overrides.
Merge Keys
The merge key <<: *anchor splices all keys from the anchored mapping into the current mapping. Keys in the current block override merged values.
# Define shared defaults db_defaults: &db_defaults adapter: postgres host: localhost port: 5432 # Development — inherits all defaults development: <<: *db_defaults database: myapp_dev # Production — inherits defaults, overrides host production: <<: *db_defaults database: myapp_prod host: db.example.com # overrides merged value
logging: &logging log_level: info log_file: app.log monitoring: &monitoring metrics: true tracing: true # Merge two anchors at once using a list service: <<: [*logging, *monitoring] name: auth-service port: 3000
When two merged anchors have the same key, the first anchor in the list wins. Keys defined locally in the mapping always take highest priority over all merged values.
Multi-Document Files
A single .yaml file can contain multiple independent documents, separated by ---. This is common in Kubernetes manifests.
--- # Document 1 starts name: Alice role: admin ... # Optional explicit end marker --- # Document 2 starts name: Bob role: viewer --- # Document 3 starts name: Carol role: editor
--- apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: my-app ports: - port: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: my-deployment spec: replicas: 3
| Marker | Meaning |
|---|---|
| --- | Start of a new document. Also separates YAML directives from document content. |
| ... | Explicit end of a document. Optional — omitting it is valid. |
Real-World Examples
YAML is the standard format for Docker Compose, GitHub Actions, Kubernetes, and Ansible. Here are practical patterns you'll encounter every day.
services: web: image: nginx:alpine ports: - "8080:80" volumes: - ./html:/usr/share/nginx/html:ro environment: - NGINX_HOST=example.com restart: unless-stopped depends_on: - api api: build: context: ./api dockerfile: Dockerfile env_file: - .env deploy: resources: limits: memory: 512M cpus: "1.0" volumes: data: driver: local networks: frontend: driver: bridge
name: CI on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Multi-line run run: | echo "Building..." npm run build echo "Done!"
--- apiVersion: apps/v1 kind: Deployment metadata: name: my-app labels: app: my-app spec: replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: app image: myrepo/my-app:latest ports: - containerPort: 8080 env: - name: ENV value: production resources: limits: memory: 256Mi cpu: "500m"
# Shared base config base: &base adapter: postgres pool: 5 timeout: 5000 development: <<: *base database: app_development host: localhost test: <<: *base database: app_test host: localhost production: <<: *base database: app_production host: db.example.com pool: 25 # overrides merged pool: 5 timeout: 10000 # overrides merged timeout
Best Practices — Always use a YAML linter (yamllint). Quote strings that start with special characters. Register custom tags in your parser. Avoid deeply nested structures — flatten where possible. Use anchors for shared blocks, not for single scalar values.