---
title: "Read Kubernetes manifests and multi-document YAML"
url: https://recursiveai.org/oneyaml/rust/cookbook/kubernetes-multi-document/
description: "Read every document of a multi-document YAML file such as a Kubernetes manifest into Rust structs, then edit one document and keep the rest byte for byte."
version: "1.2.0"
---

# Read Kubernetes manifests and multi-document YAML

A Kubernetes manifest often holds several documents separated by `---`. Read each one into a struct, or edit one of them and write the whole file back with its comments.

The file:

```yaml
# Web frontend
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: web
          image: registry.example.com/web:1.4.2 # pinned by release tooling
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  ports:
    - port: 80
      targetPort: 8080
```

The program:

```rust
use oneyaml::{Deserializer, Stream};
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Object {
    api_version: String,
    kind: String,
    metadata: Metadata,
}

#[derive(Debug, Deserialize)]
struct Metadata {
    name: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let src = std::fs::read_to_string("app.yaml")?;

    // Typed: one Deserializer per document.
    for doc in Deserializer::from_str(&src) {
        let obj = Object::deserialize(doc)?;
        println!("{} {} ({})", obj.kind, obj.metadata.name, obj.api_version);
    }

    // Lossless: edit the Deployment, keep everything else byte for byte.
    let mut stream = Stream::parse(src)?;
    let i = stream
        .documents()
        .position(|d| d.get_path("kind").and_then(|n| n.as_str()).as_deref() == Some("Deployment"))
        .ok_or("no Deployment")?;
    let mut deploy = stream.document_mut(i).ok_or("no document")?;
    deploy.set_path("spec.template.spec.containers[0].image", "registry.example.com/web:1.5.0")?;
    print!("{stream}");
    Ok(())
}
```

```text
Deployment web (apps/v1)
Service web (v1)
# Web frontend
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: web
          image: registry.example.com/web:1.5.0 # pinned by release tooling
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  ports:
    - port: 80
      targetPort: 8080
```

## Good to know

- Iterating a `Deserializer` yields one deserializer per document, in order. A document you don’t deserialize before asking for the next is skipped.
- `oneyaml::from_str` reads a single document. Given a stream with more than one, it’s an error, not a silent read of the first.
- `Stream::parse` keeps every document with its comments. `stream.documents()` gives a read view of each, and `document_mut(i)` edits one in place. Each document writes its own region of the file, so the Service’s text is untouched.
- The default limits include a cap on documents per stream. For manifests from outside, use `Limits::untrusted()` ([read untrusted YAML safely](https://recursiveai.org/oneyaml/rust/cookbook/untrusted-input/)).

The crate repository has a fuller example that edits a Deployment and a Service and checks every comment’s owner: [`k8s_manifest.rs`](https://github.com/recursive-ai-oss/oneyaml-rust/blob/v1.2.0/crates/oneyaml/examples/k8s_manifest.rs).
