---
title: "Anchors, aliases and merge keys"
url: https://recursiveai.org/oneyaml/rust/cookbook/anchors-aliases-merge-keys/
description: "Read YAML anchors and aliases in Rust, turn on << merge keys for docker-compose-style files, and follow an alias to its anchor in a Document."
version: "1.2.0"
---

# Anchors, aliases and merge keys

Anchors (`&name`) and aliases (`*name`) reuse a node. Merge keys (`<<`) copy a mapping’s entries into another, as docker-compose and many CI files do. YAML 1.2 has no merge keys, so oneyaml reads `<<` as a plain key unless you turn them on.

```rust
use oneyaml::{Document, Settings, Value};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let src = "\
defaults: &defaults
  image: nginx:1.27
  replicas: 2
web:
  <<: *defaults
  replicas: 3
worker: *defaults
";
    // Aliases are expanded on read; `<<` is a plain key in YAML 1.2.
    let v: Value = oneyaml::from_str(src)?;
    println!("worker.image = {:?}", v["worker"]["image"].as_str());
    println!("web.image without merge keys = {:?}", v["web"]["image"].as_str());

    // Turn on YAML 1.1 merge keys for docker-compose-style files.
    let v: Value = oneyaml::from_str_with(src, &Settings::new().merge_keys(true))?;
    println!("web.image = {:?}, web.replicas = {:?}", v["web"]["image"].as_str(), v["web"]["replicas"].as_u64());

    // A Document keeps anchors and aliases as written, and can follow them.
    let doc = Document::parse(src)?;
    let worker = doc.get("worker").ok_or("no worker")?;
    println!("worker is an alias of {:?}", worker.alias_name());
    let target = worker.alias_target().ok_or("no target")?;
    println!("target anchor {:?}, image {:?}", target.anchor(), target.get("image").and_then(|n| n.as_str()));
    Ok(())
}
```

```text
worker.image = Some("nginx:1.27")
web.image without merge keys = None
web.image = Some("nginx:1.27"), web.replicas = Some(3)
worker is an alias of Some("defaults")
target anchor Some("defaults"), image Some("nginx:1.27")
```

## Good to know

- `Settings::new().merge_keys(true)` turns on merge keys alone. `Compat::yaml_1_1()` turns on every YAML 1.1 behavior, merge keys included.
- Keys in the mapping win over merged ones: `web.replicas` is 3, not 2.
- **Alias expansion is budgeted.** Expanding aliases, merges included, counts toward the alias budget of `Limits`, so a “billion laughs” file fails fast ([read untrusted YAML safely](https://recursiveai.org/oneyaml/rust/cookbook/untrusted-input/)).
- **A `Document` never expands aliases.** It keeps `&` and `*` as written, and `alias_target()` follows one. An edit below an alias is refused, because it would change every place that shares the node.
- `NodeMut::push_alias`, `insert_alias` and `insert_merge` write new aliases and merge keys.

Merge keys across Go, Rust and C# libraries, measured: [YAML anchors, aliases and merge keys explained](https://recursiveai.org/oneyaml/articles/yaml-anchors-aliases-merge-keys/).
