Reduce YAML Configuration Drift with Anchors and Merge Keys
Reuse shared YAML blocks with anchors and aliases, override values with the merge key, and verify the result with your parser.
09 Nov 2025, 07:10 UTC

Why shared configuration blocks drift apart
When you run several services from similar configuration files, it is tempting to copy the same image policy, logging block, and resource limits into each one. The first change to a shared value—such as raising a memory limit—requires editing every file. Miss one, and services behave differently without any obvious error in the logs.
YAML anchors and aliases let you define a block once and reference it repeatedly in the same document. The merge key, written as <<, pulls the fields from an anchored map into the current map while letting you override individual fields locally. This keeps shared values in one place and makes overrides explicit.
How anchors, aliases, and merge keys work
An anchor marks a YAML node with an ampersand and a name, such as &base. An alias references that node later with an asterisk, such as *base. When the anchor is a mapping, the merge key inserts its key-value pairs into the current mapping. Keys defined explicitly in the local mapping generally take precedence over merged values, but behavior can vary by parser, so you should verify the rendered result rather than assume it.
Anchors are scoped to a single YAML document. An anchor defined in config.yaml cannot be referenced in production.yaml without an external pre-processor or templating tool.
Worked example: shared service defaults
The following configuration defines a hidden base template named _base. The leading underscore is a convention; it does not prevent YAML from processing the node, but some application frameworks ignore top-level keys that start with a dot or underscore when looking for top-level objects.
_base: &base
image: example/app:1.2.0
restart: unless-stopped
logging:
driver: json-file
options:
max-size: 10m
max-file: 3
web:
<<: *base
ports:
- 8080:8080
environment:
- LOG_LEVEL=debug
worker:
<<: *base
command: [python, worker.py]
environment:
- LOG_LEVEL=info
- TIMEOUT=60s
Here web and worker inherit image, restart, and logging from base. Each service adds its own ports, command, or environment variables. Because LOG_LEVEL is defined locally in each service, it overrides any value that might exist in the base block.
Limitations to keep in mind
- Single-document scope: Anchors do not work across YAML documents separated by
---, nor across separate files. You need a pre-processor such as Kustomize, Helm, or a custom script to share anchors between files. - Specification version: The merge key
<<is part of the YAML 1.1 extension, not the core YAML 1.2 specification. Most common parsers support it, but strict YAML 1.2 parsers may reject it. - Recursion risk: Deeply nested or self-referencing aliases can cause memory exhaustion in parsers without recursion limits. Keep inheritance chains shallow.
Verify the merged result
Run this Python snippet locally against your file. It requires no special permissions and uses yaml.safe_load, which does not execute arbitrary code.
import yaml
with open('config.yaml', 'r') as f:
data = yaml.safe_load(f)
print(yaml.dump(data['web'], indent=2))
print(yaml.dump(data['worker'], indent=2))
Check that web contains the base image and restart values along with its own ports and LOG_LEVEL. Confirm that worker has its own command and TIMEOUT. If a base value is missing or an override did not apply, inspect key order in your YAML file and test with your target parser before deploying.
When to use this approach
Use anchors and merge keys to remove duplication inside a single configuration file. When your configuration spans many files or environments, move shared values into a templating system such as Helm or Kustomize instead of trying to stretch YAML anchors across document boundaries.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.