Using YAML Anchors, Aliases, and Merge Keys for DRY Configuration
Learn how YAML anchors, aliases, and the merge key let you reuse configuration blocks safely, with a worked example, trade‑offs, and verification steps.
25 Sept 2025, 23:24 UTC

The problem: repeating configuration blocks
When you manage multiple services, jobs, or environments in a single YAML file, you often find yourself copying the same mapping of settings—default image tags, retry policies, or common environment variables—over and over. Duplication makes the file harder to read and increases the chance that a change in one place is forgotten elsewhere.
Thesis: reuse YAML nodes with anchors and merge keys
YAML provides two mechanisms that let you define a piece of data once and refer to it elsewhere: anchors (&) create a label for a node, and aliases (*) refer back to that same node. The merge key (<<) then lets you fold the contents of one or more mappings into another mapping, giving precedence to keys you write explicitly. Together they enable a clean “defaults‑plus‑override” pattern without copying.
Anchors and aliases: sharing a node
An anchor is attached to any YAML node:
defaults: &defaults
image: myapp:latest
retries: 3
timeout: 30s
The alias *defaults produces exactly the same mapping as the anchored node, not a copy. If you later change the anchored mapping, every alias reflects that change.
Merge key (<<): folding mappings
The merge key takes a mapping (or a sequence of mappings) and inserts its keys into the current mapping. Keys that appear explicitly in the current mapping win over merged ones:
service_a:
<<: *defaults
environment:
DEBUG: true
After merging, service_a contains image, retries, timeout from *defaults plus the explicit environment block.
Worked example: two services with shared defaults
Imagine a CI configuration where two jobs need the same Docker image and retry policy, but one job requires a longer timeout.
# Define the shared fragment once
defaults: &job_defaults
image: builder:2.1
retries: 4
jobs:
build:
<<: *job_defaults
timeout: 5m
script:
- make build
test:
<<: *job_defaults
timeout: 15m # override the merged value
script:
- make test
When a YAML 1.1‑supporting parser resolves this document, the build job ends up with image: builder:2.1, retries: 4, timeout: 5m, and the test job gets the same image and retries but a timeout of 15m. The only difference between the jobs is the overridden timeout value.
Limitations and verification steps
While anchors and merge keys reduce duplication, they introduce implicit coupling: editing the anchored node changes every consumer silently. Debugging can be harder because error messages often point to the resolved value rather than the anchor’s source. Moreover, the merge key is not part of the YAML 1.2 core specification; it is an extension that some stricter parsers may reject or treat as a literal key named "<<".
To verify that your toolchain behaves as expected:
- Create a minimal test file containing an anchor, an alias, and a merge key (as shown above).
- Load the file with each YAML parser you use (e.g., PyYAML, libyaml, Go’s yaml.v3, Jackson) and print the fully resolved data structure.
- Check the parser’s documentation to confirm whether it targets YAML 1.1 or 1.2 and whether merge keys are enabled by default.
- Perform a round‑trip: load the file and dump it again. Observe whether the output preserves the anchor/alias syntax or expands the merged keys fully.
- Test failure modes deliberately—reference an anchor from a second document separated by
---or delete the anchor definition—and verify that the error message is understandable.
If you need stricter portability, consider limiting anchors to truly identical fragments and using a schema‑validation step (e.g., JSON Schema, OpenAPI, or a custom Go struct) in your CI pipeline to assert the resolved values rather than relying solely on YAML syntax.
Putting it into practice
Start by identifying repeated blocks in your existing YAML files. Replace each block with an anchor and use aliases with a merge key where you need to override a few values. Keep the anchored node small and well‑documented so future maintainers can see the source of shared defaults. Finally, add a verification step to your build that parses the file with the exact library version you pin, ensuring that the merge key behaves as you expect across all environments.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.