YAML is data, not code
YAML is a human-friendly format for representing structured data. You will see it in Home Assistant, Docker Compose, GitHub Actions, Kubernetes, and many tool configurations. It does not “run” by itself; the application reading it gives it meaning.
The central rule: indentation is structure. A single wrong space can change the meaning or make the file invalid.
The three shapes
# Mapping: key to value
name: lounge
# List: ordered items
rooms:
- lounge
- office
# Nested mapping
climate:
target: 21
mode: heat
Use spaces, never tabs. Two spaces per nesting level is a common, readable convention.
Scalars: strings, numbers, booleans, null
port: 8080
enabled: true
name: "Lounge heater"
notes: null
Quote strings when they contain characters with special meaning, leading zeroes, dates you mean to keep as text, or values that could be mistaken for booleans. In YAML ecosystems, yes, on, off, and date-looking values can be interpreted unexpectedly depending on parser/version.
# Safer intent
pin: "01"
label: "on"
message: "Restart at 06:00"
Lists of objects
automations:
- alias: Notify when WAN is down
trigger:
- platform: state
entity_id: binary_sensor.8_8_8_8
to: "off"
action:
- service: notify.mobile_app_phone
data:
message: "WAN is unavailable"
The dash begins each list item. The nested keys underneath belong to that item.
Home Assistant: readability and correctness
alias: Lounge AC scheduled start
mode: single
trigger:
- platform: time
at: input_datetime.living_room_ac_scheduled_time
action:
- service: climate.set_temperature
target:
entity_id: climate.lounge
data:
temperature: 26
hvac_mode: heat
Guidelines:
- Prefer the HA UI for helpers and common automations when it offers the same capability.
- Use meaningful
alias,id, and entity names. - Make automation mode explicit:
single,restart,queued, orparallelchanges behaviour under repeated triggers. - Use the Developer Tools YAML/config validation before reloads.
- Build one trigger/action path first; add conditions and branches only once the baseline works.
Docker Compose YAML
services:
app:
image: example/app:1.2.3
restart: unless-stopped
environment:
TZ: Australia/Melbourne
ports:
- "8080:8080"
volumes:
- app_data:/var/lib/app
volumes:
app_data:
For services that matter, pin major/minor versions or digest deliberately; latest means a later pull can change behaviour. Keep credentials in an ignored .env file or secret manager—not in committed Compose YAML.
Validate before changes:
docker compose config # expand and validate config
docker compose up -d # apply
docker compose logs -f app # observe
Anchors: reuse only when it improves clarity
x-common: &common
restart: unless-stopped
services:
app:
<<: *common
image: example/app:1.2.3
Anchors reduce repetition, but overusing them makes a file harder to read. Prefer straightforward duplication for small, distinct services.
GitHub Actions workflow shape
name: Build site
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
Workflow YAML often has its own expression syntax (${{ ... }}) and schema. A valid YAML file can still be an invalid workflow—validate against the platform documentation and inspect action logs.
Merge vs replace: a common conceptual trap
YAML itself simply represents data. Whether a list is merged, replaced, appended, or overridden is decided by the application. For example, Compose overrides and Home Assistant includes have their own rules. Never assume “it will merge”; read the consuming tool’s documentation.
Debugging YAML quickly
- Read the error’s line and column, then look above it—the structural mistake is often earlier.
- Check alignment of every sibling key.
- Verify every list item has a dash at the same indentation level.
- Quote suspicious scalar values.
- Reduce to the smallest valid example; add blocks back one at a time.
- Use the application’s validator, not only a generic YAML checker.
Common failures
| Failure | Why | Fix |
|---|---|---|
| Tabs | YAML indentation requires spaces | Configure editor to insert spaces |
| Misaligned list dash | Changes parent/child relationship | Align - with sibling items |
| Colon inside unquoted text | Parsed as mapping syntax | Quote the complete string |
| Duplicate key | Later value may overwrite earlier one | Use linting; keep keys unique |
| Secret committed to Git | YAML looks like ordinary config | Use .env, GitHub/HA secrets, .gitignore |
| Valid YAML, broken app | App schema is wrong | Validate with app-specific tool |
A safe edit workflow
# Format/check where a tool supports it
docker compose config
# Review only your intended change
git diff -- compose.yaml
# Apply and observe
docker compose up -d
docker compose ps
docker compose logs --tail=100 app