YAML Patterns

YAML patterns and idioms for infrastructure as code - scalar types, block scalars, anchors, merge keys, and validation.

Scalar Types

Strings
# Plain (unquoted) - works most of the time
name: web-01

# Double-quoted - escape sequences work (\n, \t, \\)
message: "Line one\nLine two"

# Single-quoted - literal, no escaping
path: '/etc/nginx/nginx.conf'

# GOTCHA: these are NOT strings without quotes
yes_bool: true         # boolean
no_bool: false         # boolean
number: 42             # integer
float: 3.14            # float
null_val: null         # null
also_null: ~           # null
version: "3.10"        # string (quoted to prevent float interpretation)
port: "8080"           # string (quoted to prevent integer)
Force type with tags
force_string: !!str 123
force_int: !!int "42"
force_float: !!float "3.14"
force_bool: !!bool "true"

GOTCHA: YAML interprets yes, no, on, off as booleans. Quote them: "yes".

Block Scalars

Literal block (preserves newlines)
script: |
  #!/bin/bash
  echo "Line 1"
  echo "Line 2"
  exit 0
Folded block (joins lines, double newline = paragraph break)
description: >
  This is a very long description
  that gets folded into a single line.

  This starts a new paragraph.
Chomping indicators
# Keep trailing newline (default)
keep: |
  text

# Strip trailing newline
strip: |-
  text

# Keep all trailing newlines
chomp: |+
  text

PATTERN: | for scripts and multi-line commands. > for long descriptions.

Anchors & Merge Keys

Define and reference anchors
defaults: &defaults
  adapter: postgres
  host: localhost
  port: 5432

development:
  database: myapp_dev
  <<: *defaults

production:
  database: myapp_prod
  <<: *defaults
  host: db.prod.internal    # Override merged value
Anchor on scalar value
base_port: &port 8080

services:
  web:
    port: *port           # 8080
  api:
    port: *port           # 8080
Multiple merge keys
common: &common
  restart: always
  logging:
    driver: json-file

resources: &resources
  deploy:
    resources:
      limits:
        memory: 512M

web:
  <<: [*common, *resources]
  image: nginx:latest

GOTCHA: Anchors are a YAML feature, not specific to any tool. They work in Ansible, Docker Compose, CI configs.

Multi-Document YAML

Document separator
---
apiVersion: v1
kind: Namespace
metadata:
  name: app
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: app
data:
  key: value
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: app
Processing multi-doc YAML
# Split into individual documents
yq eval-all 'select(documentIndex == 0)' multi.yml

# Count documents
yq eval-all '[.] | length' multi.yml

# Apply all Kubernetes resources
kubectl apply -f multi.yml

USE CASE: Kubernetes manifests, Ansible playbooks with multiple plays.

Validation & Linting

Validate YAML syntax
# With yq
yq eval '.' config.yml > /dev/null

# With Python
python3 -c "import yaml; yaml.safe_load(open('config.yml'))"

# With yamllint
yamllint config.yml
yamllint -d '{extends: default, rules: {line-length: {max: 120}}}' config.yml
Convert between formats
# YAML to JSON
yq eval -o=json config.yml

# JSON to YAML
yq eval -P config.json

# Validate Ansible playbook
ansible-playbook --syntax-check site.yml

PATTERN: Validate YAML in CI before applying: yamllint && ansible-lint && ansible-playbook --syntax-check.

Quick Reference

Pattern Description

&anchor / *anchor

Define and reference an anchor

<<: *anchor

Merge key (inherit from anchor)

| (pipe)

Literal block scalar (preserves newlines)

> (greater-than)

Folded block scalar (joins lines)

|- / >-

Strip trailing newline from block

!!str 123

Force type to string

---

Document separator (multi-doc)

key: null or key: ~

Explicit null value

- { name: x, port: 80 }

Flow mapping (inline dict)

[a, b, c]

Flow sequence (inline list)