# What Are YAML Anchors? — &anchor, *alias and the <<: Merge Key Explained

Canonical: https://commareports.com/glossary/yaml-anchors
Published: 2026-10-26

> YAML anchors (&name) mark a value so an alias (*name) can reuse it later in the same file, and the <<: merge key copies a mapping into another. How they work, how overrides resolve, and where they break: GitHub Actions, GitLab includes and JSON conversion.

# YAML anchors

**A YAML anchor is a label — `&name` — that marks a value so you can reuse
it later in the same document with an alias, `*name`.** The parser
resolves aliases while reading, so your program receives plain data with
the value repeated. Anchors are YAML's own answer to copy-paste.

```yaml
defaults: &defaults # anchor the mapping
  image: node:22
  retries: 2
  timeout: 10m

unit:
  <<: *defaults # merge every key from defaults
  script: bun test

e2e:
  <<: *defaults
  timeout: 30m # local key wins over the merged one
  script: bunx playwright test

notify_on: &channels ["#ci", "#releases"]
deploy_notify: *channels # alias: the same list again
```

After parsing, `e2e` is `{ image: node:22, retries: 2, timeout: 30m,
script: … }` and `deploy_notify` is the same two-item list.

## Three pieces

| Syntax      | Name      | What it does                                                    |
| ----------- | --------- | --------------------------------------------------------------- |
| `&name`     | anchor    | Labels the value that follows it                                |
| `*name`     | alias     | Stands for the anchored value, in full                          |
| `<<: *name` | merge key | Copies the keys of an anchored mapping into the current mapping |

An alias reuses a value **as is**. You can't change one key through a plain
alias. The merge key is what lets you take a mapping and override part of
it.

## How merges resolve

- **Local keys always win.** A key written in the mapping beats the same key
  from `<<`, whether it comes before or after the merge line.
- **Several merges: earlier wins.** In `<<: [*base, *gpu]`, a key present in
  both comes from `base`.
- **Merges are shallow.** If `defaults` has `env: { A: 1 }` and you set
  `env: { B: 2 }` locally, the result is `{ B: 2 }`, not both. Nested maps
  are replaced, not combined.
- **Lists can't be merged.** `<<` only works with mappings. There is no
  YAML syntax to append to an anchored list.

## Where anchors work, and where they don't

| Where                | Anchors and aliases | `<<:` merge key | Reuse across files                    |
| -------------------- | ------------------- | --------------- | ------------------------------------- |
| Docker Compose       | yes                 | yes             | `include`, `extends`                  |
| GitLab CI            | yes                 | yes             | `extends`, `!reference` (not anchors) |
| GitHub Actions       | yes (since 2025)    | no              | reusable workflows, composite actions |
| Kubernetes manifests | yes                 | yes             | Helm, Kustomize                       |
| Helm `values.yaml`   | yes                 | yes             | values files, `_helpers.tpl`          |

Docker Compose pairs anchors with top-level `x-` keys, which Compose ignores,
so you have somewhere to put a shared block that isn't a service:

```yaml
x-logging: &logging
  driver: json-file
  options: { max-size: 10m }

services:
  api: { image: api:latest, logging: *logging }
  worker: { image: worker:latest, logging: *logging }
```

## Gotchas

- **One document only.** An anchor doesn't reach past a `---` separator, or
  into another file.
- **Define before use.** An alias must come after its anchor in the file.
- **YAML 1.2 dropped the merge key.** Most parsers (js-yaml, PyYAML, Go's
  yaml.v3, SnakeYAML) still accept it, but a strict 1.2 parser may read
  `<<` as an ordinary key.
- **Converting to JSON expands everything.** JSON has no references, so
  aliases become copies and the anchors are gone for good.
- **Alias bombs.** Nested aliases can expand exponentially (the YAML version
  of the "billion laughs" attack). Parsers that read untrusted YAML cap
  alias expansion; check that yours does.

## Seeing what an anchor actually expands to

When a merged CI job does something you didn't expect, the fastest check is
to look at the expanded data. Comma's free [YAML to JSON
converter](/tools/yaml-to-json) resolves every anchor, alias and merge key
so you can see exactly what the tool receives. [YAML diff](/tools/yaml-diff)
compares two files semantically, so refactoring repeated blocks into anchors
shows **no changes** if you got it right. Both run in your browser, and
either result can be published as a link teammates comment on.

## Try it

Comma is free: unlimited reports, unlimited commenters, unlimited revision
history.

**[Open the YAML to JSON converter →](/tools/yaml-to-json)**

### Related

- [YAML diff](/tools/yaml-diff) · [YAML to JSON](/tools/yaml-to-json) · [JSON viewer](/tools/json-viewer)
- [JSON Schema](/glossary/json-schema) · [JSON Pointer](/glossary/json-pointer)
- [Glossary](/glossary)
