YAML Anchors and Aliases Explained: DRY Configuration

May 28, 20266 min read

The Repetition Problem in YAML

Configuration files often contain repeated blocks — the same database credentials across environments, identical build steps for multiple jobs, or shared container settings across services. Copy-pasting these blocks works until one value changes and you forget to update the other three copies.

YAML anchors and aliases solve this by letting you define a block once and reference it elsewhere. Changes propagate to every reference automatically.

Anchors: Define a Reusable Block

An anchor marks a node with a name using &:

defaults: &defaults
  timeout: 30
  retries: 3
  log_level: info

The &defaults anchor attaches the name defaults to that mapping. The node still appears in the final data — anchoring doesn't remove it.

Aliases: Reference an Anchored Block

An alias references an anchored node using *:

defaults: &defaults
  timeout: 30
  retries: 3
  log_level: info

production:
  <<: *defaults
  log_level: warning
  database: prod-db

staging:
  <<: *defaults
  database: staging-db

The *defaults alias copies the anchored mapping. Combined with the merge key <<, it merges the anchored keys into the current mapping.

The Merge Key (<<)

The merge key << inserts all keys from an aliased mapping into the current mapping. Keys defined explicitly in the current mapping override merged keys:

production:
  <<: *defaults       # Merges timeout, retries, log_level
  log_level: warning  # Overrides the merged log_level
  database: prod-db   # Adds a new key

Result after parsing:

production:
  timeout: 30
  retries: 3
  log_level: warning  # Overridden
  database: prod-db   # Added

Merging Multiple Anchors

You can merge from multiple anchors:

base: &base
  image: node:20
  working_dir: /app

cache: &cache
  cache:
    paths:
      - node_modules/

build:
  <<: [*base, *cache]
  script: npm run build

Later anchors override keys from earlier ones when both define the same key.

Anchors Without Merging

Anchors work with any YAML node type, not just mappings:

Anchored Sequences

common_steps: &common_steps
  - checkout
  - install dependencies

deploy_production:
  steps:
    - *common_steps
    - deploy to production

Note: *common_steps as a list item creates a nested list. To flatten it, structure your anchors differently or use merge keys.

Anchored Scalars

api_version: &apiVersion v1

service1:
  apiVersion: *apiVersion

service2:
  apiVersion: *apiVersion

Useful for values that appear in many places and might change together — like API versions or image tags.

Real-World Patterns

GitHub Actions

defaults: &_step_defaults
  working-directory: ./app

install: &install
  <<: *step_defaults
  run: npm ci

lint:
  <<: *step_defaults
  needs: install
  run: npm run lint

test:
  <<: *step_defaults
  needs: install
  run: npm test

Kubernetes

common_labels: &common_labels
  app.kubernetes.io/part-of: myapp
  app.kubernetes.io/managed-by: helm

deployment:
  metadata:
    labels:
      <<: *common_labels
      app.kubernetes.io/component: api

Docker Compose

x-common: &common
  restart: unless-stopped
  logging:
    driver: json-file
    options:
      max-size: "10m"

services:
  web:
    <<: *common
    image: myapp-web
    ports:
      - "3000:3000"

  worker:
    <<: *common
    image: myapp-worker

The x- prefix is a Docker Compose convention for extension fields — they don't appear in the final config but serve as anchor containers.

Limitations

  • No circular references: An alias cannot reference an anchor defined within the same node it merges into.
  • Merge key only works with mappings: You can't use << with sequences or scalars.
  • Parser support varies: Some YAML parsers (especially older ones) don't support merge keys. PyYAML supports them; js-yaml supports them; Go's gopkg.in/yaml.v2 supports them.
  • No partial overrides of nested mappings: The merge key operates at the top level. If you merge a mapping with a nested database: key, you can't override just database.host — you must override the entire database: block.

Key Takeaways

  • Anchors (&name) mark a node for reuse; aliases (*name) reference it
  • The merge key (<<) inserts keys from an anchored mapping, with local keys taking precedence
  • Anchors work with any node type: mappings, sequences, and scalars
  • Use extension fields (x-) in Docker Compose to hold anchors without polluting the output
  • Merge key limitations: no nested partial overrides, mappings only, parser support varies
  • Anchors and aliases are a parsing-level feature — the consuming application sees only the resolved data

Try It Yourself

Convert between JSON and YAML instantly with our free JSON-YAML Converter. Paste your config, see the anchors resolved, and switch formats in one click.