Data formats · August 28, 2026

YAML anchors and aliases explained (with copy-paste examples)

YAML anchors (&) and aliases (*) let you reuse config without copy-paste. Here's how they work, when to use them, and the gotchas that bite people in production.

If you’ve ever copy-pasted the same 10 lines of YAML into three different places in a config file, you’ve felt the pain YAML anchors are designed to solve. An anchor lets you define a value once and reference it from anywhere in the same document. Change the value in one place, and every reference updates.

This post walks through how anchors and aliases work, when to use them, and the edge cases that catch people out.

The basic syntax

A YAML anchor is a name you give to a node:

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

The &defaults is the anchor. It attaches to the value of defaults, which is a map. You can put anchors on any node — a string, a number, a list, a map, a sequence.

An alias is a reference to an anchor:

production:
  <<: *defaults
  region: us-east-1
  replicas: 5

staging:
  <<: *defaults
  region: us-west-2
  replicas: 2

The <<: *defaults is the “merge key”. It says “take everything from the anchor called defaults and merge it into this map.” The result is the same as if you’d written out the contents of defaults inline:

production:
  retries: 3
  timeout: 30
  log_level: info
  region: us-east-1
  replicas: 5

But if you change retries in defaults, every place that uses the anchor updates. That’s the whole point.

Anchors on single values

Anchors don’t have to merge into a map. You can anchor a single value and reference it:

api_version: &api "v2.1.0"
services:
  auth:
    version: *api
  billing:
    version: *api
  reports:
    version: *api

Now changing the version of the API is a one-line change. The references all follow.

You can also anchor a list:

allowed_origins: &origins
  - https://app.example.com
  - https://admin.example.com
  - https://staging.example.com

cors:
  web: *origins
  api: *origins

The list is reused verbatim.

When anchors help

Anchors shine when you have shared defaults across multiple instances. Kubernetes manifests, docker-compose files, and CI matrices are full of this pattern.

A note for 2026: Helm and Kustomize dominate Kubernetes templating now, and they operate outside YAML’s anchor system — they generate YAML, they don’t parse it. If you’re on Helm or Kustomize, you don’t need anchors (you have values.yaml and patch overlays instead). Anchors are still the right call for plain kubectl apply -f workflows, GitHub Actions matrices, docker-compose files, and any hand-authored config that doesn’t go through a template engine.

A Kubernetes example — three services with the same resource limits:

base_resources: &base_resources
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi

apiDeployment:
  spec:
    template:
      spec:
        containers:
          - name: api
            image: myapp/api:1.2.3
            resources: *base_resources

workerDeployment:
  spec:
    template:
      spec:
        containers:
          - name: worker
            image: myapp/worker:1.2.3
            resources: *base_resources

A docker-compose example — same environment for dev and prod:

common_env: &common_env
  DATABASE_URL: postgres://db.internal/app
  REDIS_URL: redis://cache.internal:6379
  LOG_LEVEL: info

services:
  api:
    environment:
      <<: *common_env
      ENVIRONMENT: dev
  worker:
    environment:
      <<: *common_env
      ENVIRONMENT: dev

In both cases, changing the common env is a one-line edit.

The gotchas

Anchors are powerful but have a few sharp edges.

Anchors are local to a document. A multi-document YAML file (with --- separators) has its own scope for anchors. An anchor defined in document one cannot be referenced from document two. If you’re using tools that produce multi-doc YAML (like kubectl get -o yaml with multiple resources), you can’t use anchors to share values across them.

Anchors and merging interact with overrides in surprising ways. If both the anchor and the referencing map define the same key, the referencing map’s value wins:

defaults: &defaults
  retries: 3
  timeout: 30

production:
  <<: *defaults
  retries: 10  # this wins

So production.retries is 10, not 3. That’s the expected behaviour, but it bites people who expect “last one wins” or “first one wins” depending on the order.

Keys in merged maps can’t be removed. If defaults has a legacy_setting: true key and you want to “un-set” it in production, you can’t. Merge only adds keys; it never removes them. Workaround: don’t include the key in the anchor.

Anchors can’t reference themselves. A self-referential anchor creates a cycle. Most parsers detect this and throw an error. If you ever get a “recursive anchor” error, look for an alias pointing back to the anchor that contains it.

Anchors and JSON don’t mix. JSON has no concept of anchors. If you convert YAML to JSON with anchors, the anchors are expanded (the JSON gets larger) or dropped (the JSON is smaller). Either way, the result is bigger than the YAML. If you need a JSON version, you usually need a different config source.

Some linters complain about anchors. yamllint defaults to disabling anchors (anchors: disable) for style reasons. If you’re using anchors, you’ll need to either configure your linter or accept the warnings. Most teams that use anchors consider them worth the lint friction.

Anchors work on the parsed object graph, not the text. If you sort keys alphabetically with a tool like yq and then re-serialise, anchors are preserved by reference, not by the visible key order. The output is still correct, but it can be visually surprising — the anchor shows up in one place, the alias in another, and they refer to the same object.

Verifying anchors in your config

The fastest way to check that your anchors are working is to load the YAML in a parser that resolves them and print the result. The YAML validator on DevSpeedTools shows the resolved object graph — if your anchors are wired up correctly, you’ll see the same values everywhere.

For more involved inspection, the YAML statistics tool tells you the total number of unique vs. duplicated keys, which is a good proxy for “are my anchors actually deduplicating anything?”

For an interactive editor experience, VS Code with the Red Hat YAML extension will resolve anchors on hover and show you where each alias points.

The TL;DR

Anchors are YAML’s version of a variable. They work at parse time, not at serialise time, so the file you write contains the anchor once and the references in many places. The parser expands them to the same value.

Use them for shared defaults. Don’t use them to try to share state across documents. Don’t expect JSON conversion to preserve them. And when in doubt, run the file through a validator to confirm the expansion matches your intent — anchors that silently produce the wrong value are the kind of bug that ships to production and lives there for a year.