YAML Anchors, Aliases, and Merge Keys: Cut Configuration Duplication in CI & Compose
Anchors, aliases, and merge keys let you declare a value once in YAML and reuse it, cutting duplication in CI and Docker Compose files. Learn the syntax, see a concrete example, and avoid common pitfalls.
07 Jan 2026, 05:04 UTC

Why Duplication Happens
In CI pipelines, Docker Compose files, and other declarative configs, we often copy the same set of keys across many jobs or services. This copy‑paste pattern inflates file size, hides subtle differences, and makes maintenance error‑prone. The YAML language offers three language‑level primitives—anchors, aliases, and merge keys—to declare a value once and reuse it elsewhere in the same document.
What Anchors, Aliases, and Merge Keys Are
Anchor (&name) marks a node so it can be referenced later. The anchor is attached to the node’s value, not its key. Alias (*name) is a reference to that anchored node; when the YAML parser loads the file, the alias is replaced with a copy of the anchored content.
Merge key (<<: *name) is a YAML 1.1 convention that allows a mapping to inherit all key/value pairs from another mapping. Local keys that appear after the merge override any duplicates. The merge key is only meaningful inside a mapping, not a scalar.
Syntax Cheat Sheet
- Define an anchor:
key: &anchorName value - Reference an alias:
key: *anchorName - Merge a mapping:
<<: *anchorNameinside another mapping - Deep merge chain:
<<: *base1, *base2(order matters)
Practical Example: Docker Compose Service Defaults
Below is a typical docker-compose.yml that defines a base service configuration and then extends it for web and worker services. The base is anchored once, and each service merges it while adding or overriding keys.
# docker-compose.yml
services:
base_service: &base
image: myapp:latest
environment:
- DEBUG=false
restart: always
web:
<<: *base
ports:
- "80:80"
environment:
- DEBUG=true
worker:
<<: *base
command: ["python", "worker.py"]
When the Compose CLI resolves the file, the web service ends up with:
- image: myapp:latest
- environment: [DEBUG=false, DEBUG=true] – the last value wins
- restart: always
- ports: ["80:80"]
Similarly, the worker service inherits the image and restart policy but replaces the command.
Common Pitfalls and Trade‑offs
- Scope is per document. Anchors cannot be shared across YAML files. If you split a Compose file into multiple files with
extends, the anchors in one file are invisible to the other. - Parser support is not guaranteed. The merge key is a YAML 1.1 convention; the 1.2 spec does not formalize it. Popular libraries (PyYAML, SnakeYAML, Go yaml.v3) still support it, but stricter tools may reject it.
- Readability suffers with deep merge chains. If a service merges three or more anchors, you must jump around the file to understand the final mapping. Keep merge chains shallow and document your intent.
- Linters may complain about duplicate keys. Some CI platforms enforce strict YAML validation and will reject files that appear to duplicate keys after merge resolution. Test with the platform’s linter before committing.
- Alias mutation is rare but possible. Most loaders resolve aliases to plain values, so mutating the resulting data structure does not affect the original anchor. However, if you use a library that preserves references, accidental mutation could propagate.
How to Verify Your Parser Supports Merge Keys
- Create a tiny YAML file that uses
<<: *anchorand load it with the library you plan to use. For example, in Python:import yaml with open("tmp.yml") as f: data = yaml.safe_load(f) print(data) - Run the CI platform’s linter (e.g.,
docker compose configorgitlab-ci-lint) to see the fully resolved configuration. If the linter fails, the platform may not support merge keys. - Check the library’s documentation for a section on merge keys or YAML 1.1 support. Look for flags like
Loader=yaml.FullLoaderin PyYAML oryaml.v3in Go.
Take‑away
Anchors, aliases, and merge keys are powerful, language‑level tools for reducing duplication in YAML configurations. Use them to:
- Declare a base configuration once and reuse it across services or jobs.
- Override only the keys that differ, keeping the rest untouched.
- Keep your files small and easier to audit.
But remember: they are confined to a single document, not all YAML versions, and deep merge chains can hurt readability. Test your parser, run the platform linter, and keep merge chains shallow. With those checks in place, you’ll enjoy cleaner configs and fewer copy‑paste bugs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.