Advertisement
← Back to Articles

YAML for Configuration: A Practical Guide

Published February 8, 2026

Why YAML Dominates Configuration

Every modern infrastructure tool uses YAML: Kubernetes, Docker Compose, Ansible, GitHub Actions, GitLab CI, Terraform variable files, Helm charts. Why? Because YAML prioritizes human readability over parser simplicity — it drops JSON's braces, brackets, and mandatory quoting in favor of indentation and bare words.

That readability comes at a cost: YAML's spec (over 80 pages, versus a one-page JSON grammar) hides genuine gotchas that silently corrupt config files. This guide covers the syntax you need day-to-day, and the specific traps that catch experienced engineers.

Advertisement

YAML Syntax Essentials

YAML uses indentation to define hierarchy — two spaces per level is the near-universal convention, and tabs are outright forbidden by the spec (most parsers throw a hard error). A mapping (object) and a sequence (array) look like this:

service: name: api-gateway port: 8080 replicas: 3 tags: - production - public-facing healthcheck: path: /health interval: 30s

The same structure as JSON: service is an object with scalar fields (name, port), a sequence (tags), and a nested object (healthcheck) — just without the punctuation.

Block Scalars: Multi-Line Strings

Configuration files often need to embed multi-line text — a shell script, a SQL query, a certificate. YAML has two block scalar styles that behave very differently:

Literal Block (|) — preserves newlines

script: | #!/bin/bash echo "Starting deploy" kubectl apply -f app.yaml

Folded Block (>) — folds newlines to spaces

description: > This service handles all inbound API traffic for the app.

Mixing these up is a common source of broken shell scripts embedded in CI YAML: a folded block (>) will silently collapse your multi-line script into one space-joined line, which usually still "parses" but runs the wrong command.

Anchors and Aliases: Keeping Config DRY

YAML lets you define a block once with an anchor (&name) and reuse it with an alias (*name) — this is how CI pipelines share environment config across jobs without copy-pasting:

defaults: &defaults adapter: postgres timeout: 30 development: <<: *defaults database: app_dev test: <<: *defaults database: app_test

The << merge key applies everything from *defaults, then lets each environment override or add keys. Both development and test end up with adapter and timeout without repeating them.

The Gotchas That Break Real Config Files

✗ The "Norway Problem" — implicit typing

Unquoted NO, YES, ON, OFF, TRUE, and FALSE are all parsed as booleans by YAML 1.1 parsers (used by most real-world tools). This is infamous for turning a country code into a boolean:

country: NO # parsed as boolean "false", not the string "Norway"

Fix: always quote ambiguous scalars — country: "NO".

✗ Version numbers become floats

version: 1.10 is parsed as the number 1.1, silently dropping the trailing zero. If a version string needs to preserve exact formatting, quote it: version: "1.10".

✗ Tabs are illegal for indentation

Unlike most languages, YAML's spec forbids tabs for indentation entirely — a single stray tab character (often pasted in from another file) throws a hard parse error rather than a warning. Configure your editor to insert spaces for YAML files specifically.

✗ Unsafe deserialization (security)

Some YAML libraries (notably older PyYAML defaults) support tags that instantiate arbitrary language objects from the document itself — loading untrusted YAML with a non-safe loader can lead to code execution. Always use the "safe load" function of your language's YAML library (e.g. yaml.safe_load() in Python) when parsing YAML from an external or user-supplied source.

✗ Trailing whitespace changes meaning

A trailing space after a key's colon, or inconsistent indentation by a single column, can silently nest a key under the wrong parent instead of failing outright — because YAML's indentation rules are structural, not just cosmetic like in most languages.

Multi-Document Files

A single YAML file can contain multiple documents separated by ---. Kubernetes uses this constantly to define several resources (a Deployment and a Service, say) in one manifest:

apiVersion: apps/v1 kind: Deployment metadata: name: api spec: replicas: 2 --- apiVersion: v1 kind: Service metadata: name: api-svc spec: ports: - port: 80

Best Practices for Configuration

1. Consistent Indentation (2 spaces)

Define it in .editorconfig and enforce it in CI with yamllint so a misaligned commit fails the build instead of production.

2. Quote Ambiguous String Values

Quote anything that looks like a boolean, number, null, or date but is meant to be a string — country codes, version strings, ports read as env-var placeholders.

3. Use Anchors for DRY Config

Don't repeat configuration blocks across environments or CI jobs — use anchors and merge keys (& / * / <<) instead.

4. Add Comments Liberally

YAML supports # comments where JSON has none — use them to explain why a value is set, not just what it is.

5. Validate Against a Schema

Use tool-specific schema validation (Kubernetes' kubeconform, a JSON Schema for custom configs) before deploying, since YAML's own type system won't catch a misspelled key.

6. Use Environment-Specific Overrides

Define a base config and override only what changes per environment (prod/staging/dev), rather than maintaining three full copies that drift apart.

Converting and Validating YAML

If you need to move data between YAML and JSON — for example, exporting a Kubernetes ConfigMap as JSON for a script, or converting a JSON API response into a YAML config file — use SmartJson's YAML formatter and validator to catch indentation and type errors like the ones above before they reach production, entirely in your browser with nothing uploaded to a server.

Key Takeaways

  • YAML prioritizes human readability over strict syntax, at the cost of a much larger spec than JSON
  • Indentation is structural — two spaces per level is standard, and tabs are a hard parse error
  • Literal blocks (|) preserve newlines; folded blocks (>) collapse them to spaces — don't mix them up in scripts
  • Quote any scalar that could be misread as a boolean, number, or null (the "Norway problem")
  • Use anchors (&) and aliases (*) to keep multi-environment config DRY
  • Always use your language's "safe load" function when parsing YAML from an untrusted source
  • Validate config against tool schemas before deployment — YAML's parser won't catch a misspelled key
Advertisement