YAML Anchors and Aliases: Reduce Duplication in Your Config Files

5 min read

YAML has a feature JSON has no equivalent for at all: the ability to define a block of data once and reuse it elsewhere in the same document, without copy-pasting it. That's what anchors and aliases do, and they show up constantly in CI/CD configs and Kubernetes manifests once you know to look for them.

The problem: repeated config blocks

Say you have several CI jobs that all need the same base setup:

jobs:
  test:
    image: node:20
    env:
      NODE_ENV: test
    timeout: 10
  lint:
    image: node:20
    env:
      NODE_ENV: test
    timeout: 10
  build:
    image: node:20
    env:
      NODE_ENV: test
    timeout: 15

Three jobs, the same image and env repeated three times. If that base image ever changes, you're editing it in three places and hoping you don't miss one.

Defining an anchor with &

An anchor (&name) marks a value as reusable. Reference it elsewhere with an alias (*name), and YAML expands it into a full copy of the original value at parse time:

defaults: &base_job
  image: node:20
  env:
    NODE_ENV: test

jobs:
  test:
    <<: *base_job
    timeout: 10
  lint:
    <<: *base_job
    timeout: 10
  build:
    <<: *base_job
    timeout: 15

The <<: *base_job syntax is the "merge key" convention — it merges the anchored mapping's keys into the current mapping, rather than nesting it under a defaults key. Now the base image lives in exactly one place; change it once, and every job that merges *base_job picks up the change.

What it looks like after expansion

Anchors and aliases are a YAML-authoring convenience only — they don't survive as a concept once parsed. Converting the config above with our YAML to JSON tool expands every alias into its full value, the same way any YAML parser would:

{
  "jobs": {
    "test": { "image": "node:20", "env": { "NODE_ENV": "test" }, "timeout": 10 },
    "lint": { "image": "node:20", "env": { "NODE_ENV": "test" }, "timeout": 10 },
    "build": { "image": "node:20", "env": { "NODE_ENV": "test" }, "timeout": 15 }
  }
}

The JSON has no memory of which fields came from an anchor versus which were written directly — by the time you have structured data, the deduplication has already served its purpose (making the source file maintainable) and its job is done.

Anchoring more than mappings

Anchors work on any YAML value, not just objects — a reusable list of allowed values, a single string, a number. A common pattern is anchoring a list and reusing it across several keys that all need the same enum-like set of values, avoiding the same copy-paste-drift problem in a different shape.

Where you'll actually encounter this

  • Docker Compose — sharing common service configuration (restart policies, networks) across multiple services.
  • GitHub Actions / GitLab CI — sharing job steps, environment variables, or runner configuration across a matrix of similar jobs.
  • Kubernetes manifests — less common than in CI configs, but used for shared labels or resource limits across multiple resource definitions in the same file.

If you're the one authoring the YAML, anchors are worth reaching for the moment you notice yourself copy-pasting a block for the second time. If you're just trying to understand what a config file actually resolves to, converting it to JSON with the tool above collapses all the anchors and shows you the real values in place.

Try YAML to JSON now

Convert YAML configs back to structured JSON.

Open YAML to JSON