Als je ooit een YAML-blok van de ene naar de andere plek in een bestand hebt gekopieerd, heb je al het probleem gevonden dat anchors en aliases oplossen. Grote Kubernetes-configs, multi-environment Docker Compose-bestanden en complexe CI-pipelines hebben de neiging dezelfde blokken te herhalen — resourcelimieten, omgevingsvariabelen, volume-mounts — keer op keer. YAML heeft hiervoor een ingebouwde oplossing, en de meeste ontwikkelaars weten niet dat die bestaat.

Anchors (&) en aliases (*) zijn het YAML-mechanisme voor het eenmalig definiëren van een waarde en het er elders in hetzelfde bestand naar verwijzen. Beschouw een anchor als het declareren van een variabele en een alias als het gebruiken ervan. De merge key (<<) breidt dit uit naar object-merging — vergelijkbaar met JavaScript's spread-operator. Beide functies zijn gedefinieerd in de YAML 1.2-specificatiesectie over node-anchors.

Basissyntaxis voor anchor en alias

De syntaxis is minimaal. Prefix een waarde met &naam om een anchor te maken, gebruik dan *naam overal in het document om ernaar te verwijzen:

yaml
# Define the anchor once
default_timeout: &timeout 30

# Reference it multiple times
services:
  api:
    timeout: *timeout      # → 30
  worker:
    timeout: *timeout      # → 30
  scheduler:
    timeout: *timeout      # → 30

Wanneer een YAML-parser dit leest, wordt elke alias omgezet naar precies dezelfde waarde als de anchor. Wijzig de anchor en alle aliases worden bijgewerkt. Geen zoek-en-vervang nodig.

Objecten samenvoegen met de <<-sleutel

Scalair hergebruik is nuttig, maar de echte kracht komt van het samenvoegen van volledige mapping-blokken. De merge key << absorbeert alle sleutel-waardeparen van de gerefereerde anchor in de huidige mapping:

yaml
# Define a base configuration block
base_service: &base
  restart: unless-stopped
  logging:
    driver: json-file
    options:
      max-size: "10m"
      max-file: "3"
  networks:
    - app-network

# Merge it into specific services
services:
  api:
    <<: *base              # merge all keys from base_service
    image: my-api:latest
    ports:
      - "8080:8080"

  worker:
    <<: *base              # same base config
    image: my-worker:latest
    command: ["node", "worker.js"]

  scheduler:
    <<: *base              # and again
    image: my-scheduler:latest
    command: ["node", "cron.js"]

Sleutels die direct in de mapping zijn gedefinieerd overschrijven sleutels van de samengevoegde anchor. Dus als base restart: unless-stopped instelt en de service ook restart: no instelt, wint de waarde op serviceniveau.

Voor en na: Docker Compose in de praktijk

Zo ziet een typisch multi-service Docker Compose-bestand eruit zonder anchors — vol herhaling:

yaml
# BEFORE — repetitive and hard to maintain
version: "3.9"
services:
  api:
    restart: unless-stopped
    env_file: .env
    networks: [app-network]
    logging:
      driver: json-file
      options:
        max-size: "10m"
    depends_on:
      - postgres

  worker:
    restart: unless-stopped
    env_file: .env
    networks: [app-network]
    logging:
      driver: json-file
      options:
        max-size: "10m"
    depends_on:
      - postgres

  mailer:
    restart: unless-stopped
    env_file: .env
    networks: [app-network]
    logging:
      driver: json-file
      options:
        max-size: "10m"
    depends_on:
      - postgres

Nu met anchors en de merge key — elk herhaald blok is exact één keer gedefinieerd:

yaml
# AFTER — DRY and easy to maintain
version: "3.9"

x-common: &common
  restart: unless-stopped
  env_file: .env
  networks: [app-network]
  logging:
    driver: json-file
    options:
      max-size: "10m"
  depends_on:
    - postgres

services:
  api:
    <<: *common
    image: my-api:2.4.1
    ports:
      - "8080:8080"

  worker:
    <<: *common
    image: my-worker:2.4.1
    command: ["node", "worker.js"]

  mailer:
    <<: *common
    image: my-mailer:2.4.1
    command: ["node", "mailer.js"]
Pro-tip: Het x--prefix op extensievelden (zoals x-common) is een Docker Compose-conventie die voorkomt dat Compose het blok probeert te verwerken als een servicedefinitie. Het wordt genegeerd door de Compose-engine maar is volledig bruikbaar als YAML-anchor.

GitHub Actions: matrix strategy met anchors

CI-configuratie is nog een plek waar anchors renderen. Hier is een GitHub Actions-workflow die gemeenschappelijke stepconfiguratie hergebruikt over meerdere jobs:

yaml
name: Build and Test

on: [push, pull_request]

# Shared setup steps
x-setup-steps: &setup-steps
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with:
      node-version: "20"
      cache: npm
  - run: npm ci

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - *setup-steps
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    steps:
      - *setup-steps
      - run: npm test -- --coverage

  build:
    runs-on: ubuntu-latest
    steps:
      - *setup-steps
      - run: npm run build

Let op: de YAML-parser van GitHub Actions ondersteunt anchors en aliases. Sommige CI-systemen (met name CircleCI's config.yml vóór versie 2.1) hadden hun eigen anchor-achtige extensiesyntaxis. Raadpleeg altijd de documentatie van je specifieke tool.

Kubernetes: resourcelimieten hergebruiken

In Kubernetes-manifesten zijn resourcelimieten en omgevingsvariabelen uitstekende kandidaten voor anchors. Hier is een patroon dat ik regelmatig gebruik:

yaml
# Anchors at the top of the file (or in a shared ConfigMap generator)
x-resources:
  small: &resources-small
    requests:
      memory: "64Mi"
      cpu: "50m"
    limits:
      memory: "256Mi"
      cpu: "200m"
  large: &resources-large
    requests:
      memory: "256Mi"
      cpu: "200m"
    limits:
      memory: "1Gi"
      cpu: "1000m"

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  template:
    spec:
      containers:
        - name: frontend
          image: frontend:latest
          resources: *resources-small    # reuse small profile

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  template:
    spec:
      containers:
        - name: api
          image: api:latest
          resources: *resources-large    # reuse large profile

Meerdere merges en overschrijvingsprioriteit

Je kunt samenvoegen vanuit meerdere anchors in één enkele mapping. Geef ze op als een lijst onder <<. Eerdere vermeldingen in de lijst hebben prioriteit boven latere voor dubbele sleutels:

yaml
defaults: &defaults
  color: blue
  size: medium
  weight: light

overrides: &overrides
  color: red          # will override defaults.color
  weight: heavy       # will override defaults.weight

result:
  <<: [*overrides, *defaults]    # overrides takes priority
  name: widget
# Result: color=red, size=medium, weight=heavy, name=widget

Beperkingen om te kennen

  • Alleen hetzelfde bestand. Anchors en aliases werken alleen binnen één YAML-documentstroom. Je kunt niet verwijzen naar een anchor uit een ander bestand. Voor hergebruik over bestanden heen heb je het eigen mechanisme van je tool nodig (bijv. Helm-templates, Kustomize, of YAML-processors zoals ytt).
  • Geen berekende waarden. Aliases worden opgelost tot exacte kopieën van de anchorwaarde. Er is geen interpolatie of templatelogica — daarvoor zijn tools zoals Helm of Jinja2.
  • Merge key is een YAML-extensie. De merge key << is gedefinieerd in een YAML 1.1-extensie, niet in de kernspecificatie. De meeste parsers ondersteunen het, maar strikt specificatieconforme YAML 1.2-parsers mogelijk niet.
  • Circulaire verwijzingen breken parsers. Probeer niet een node te verankeren en er dan van binnenuit naar te verwijzen. Het is technisch ongeldig.
  • Aliases kopiëren, refereren niet. Het wijzigen van een opgeloste aliaswaarde in je code verandert de anchor of andere aliases niet. Het zijn onafhankelijke waarden na het parsen.

Samenvatting

YAML-anchors en -aliases zijn een van de meest ondergebruikte functies van het formaat. In elk YAML-bestand langer dan ongeveer 50 regels waarbij hetzelfde configuratieblok meer dan twee keer voorkomt, horen anchors er bijna zeker in. Het verschil in onderhoudbaarheid voor en na is dramatisch — één resourcelimietprofiel of één gedeelde omgevingsvariabele wijzigen in plaats van door het hele bestand te zoeken. Gebruik de YAML Formatter om je bestanden met anchors netjes te houden, en de YAML Validator om te bevestigen dat je anchorreferenties correct worden opgelost.