How to Serve Multiple Cluster Snapshots Simultaneously with crust-gather

crust-gather's HTTP API can expose any number of collected snapshots concurrently through a single instance by automatically discovering archive directories and routing requests via URL path segments.

crust-gather is a Rust-based tool for collecting and serving Kubernetes cluster snapshots. Serving multiple cluster snapshots simultaneously requires only pointing the --archive flag at a parent directory containing multiple snapshot subdirectories, each marked by a version.yaml file.

How Multi-Snapshot Serving Works

The architecture relies on three core components working together: automatic archive discovery, in-memory state management, and path-based routing.

Archive Discovery with ArchiveSearch

When the serve command initializes, it delegates path walking to ArchiveSearch. Implemented in src/gather/writer.rs (lines 72-88), this component recursively scans the supplied --archive directory. Every subdirectory containing a version.yaml file is instantiated as an Archive object. The system collects these into a Vec<Archive> without requiring explicit enumeration of each snapshot.

Server State Architecture

The Api::new constructor in src/gather/server.rs (lines 69-76) transforms the archive vector into the server's state. It builds a HashMap<String, ArchiveReader> where each key is archive.name()—the leaf directory name—and each value is a ready-to-read ArchiveReader. This map lives in ApiState, allowing a single Actix-Web instance to hold all snapshots in memory concurrently.

URL Routing and Isolation

All API handlers use a {server} path prefix (e.g., /{server}/api/v1/pods). As shown in src/gather/server.rs (lines 10-18), the handler extracts this segment and performs a lookup in state.archives[server] to retrieve the correct ArchiveReader. This design isolates each snapshot behind its own URL namespace without requiring separate ports or processes. Additionally, prepare_kubeconfig (lines 58-82) injects a temporary kubeconfig context named after each archive, enabling client code to address any snapshot transparently.

Directory Structure Requirements

To enable automatic discovery, organize snapshots as subdirectories under a common parent. Each subdirectory must contain a version.yaml file to be recognized as a valid archive.

/path/to/snapshots/
├── production-2024-01/
│   ├── version.yaml
│   └── ... (cluster resources)
├── staging-2024-01/
│   ├── version.yaml
│   └── ...
└── development-2024-01/
    ├── version.yaml
    └── ...

The leaf directory names (production-2024-01, staging-2024-01, etc.) become the URL path segments for routing.

Step-by-Step Implementation

Follow these steps to serve multiple snapshots simultaneously:

  1. Collect or stage your snapshots in a parent directory structure as shown above.

  2. Start the server pointing at the parent directory:

    crust-gather serve --archive=/path/to/snapshots

    The --archive flag accepts only one path; crust-gather automatically discovers all valid subdirectories.

  3. Access specific snapshots via their directory name in the URL:

    http://localhost:8080/production-2024-01/api/v1/pods
    http://localhost:8080/staging-2024-01/api/v1/nodes
  4. Optional: Configure the listening socket by adding --socket=0.0.0.0:9095 to bind to a specific address and port.

Code Examples

Collect two separate cluster snapshots:

crust-gather collect --archive=snapshots/production-2024-01
crust-gather collect --archive=snapshots/staging-2024-01

Start a single server instance to expose both:

crust-gather serve \
  --archive=/absolute/path/to/snapshots \
  --socket=0.0.0.0:8080

Query specific snapshots using curl:

curl http://localhost:8080/production-2024-01/api/v1/pods | jq .
curl http://localhost:8080/staging-2024-01/api/v1/nodes | jq .

Programmatic usage with the internal API:

// Discover archives and serve them programmatically
let archives: Vec<_> = ArchiveSearch::from("/path/to/snapshots").into();
let api = Api::new(
    archives, 
    Socket("0.0.0.0:8080".parse().unwrap(), None)
).await?;
api.serve().await?;

Key Source Files and Architecture

Understanding the source code helps customize the behavior:

  • src/cli.rs (lines 96-104): Parses the --archive flag in the Server CLI implementation and passes the path to the API initialization.
  • src/gather/writer.rs (lines 72-88): Implements ArchiveSearch to discover every snapshot directory containing version.yaml.
  • src/gather/server.rs (lines 69-76, 10-18): Builds the ApiState with the archive map and handles the {server} URL segment routing.
  • src/gather/representation.rs: Defines the Archive struct and the name() method that supplies the URL key.
  • src/gather/server.rs (lines 58-82): Contains prepare_kubeconfig for injecting temporary kubeconfig contexts per archive.

Summary

  • crust-gather serves multiple snapshots through a single Actix-Web instance by mapping directory names to URL paths.
  • The ArchiveSearch component in src/gather/writer.rs automatically discovers all valid snapshots under the --archive path.
  • Each snapshot is registered in a HashMap<String, ArchiveReader> where the key is the subdirectory name.
  • Requests are routed using the {server} path segment (e.g., /snapshot-name/api/v1/pods).
  • No additional ports, processes, or configuration files are required to serve multiple snapshots simultaneously.

Frequently Asked Questions

How does crust-gather discover multiple snapshots?

crust-gather uses the ArchiveSearch implementation in src/gather/writer.rs to recursively walk the directory supplied to --archive. It identifies valid snapshots by detecting version.yaml files and automatically creates an Archive object for each matching directory.

Can I serve snapshots from different parent directories?

The serve command accepts only one --archive argument. To serve snapshots from different locations, you must first copy or symlink them into a single parent directory structure. The server initializes once per invocation and scans only the specified path.

Is there a limit to how many snapshots I can serve?

There is no hardcoded limit in the crust-gather source code. The practical limit depends on available system memory, as each snapshot loads an ArchiveReader into the ApiState hash map maintained by the Actix-Web server. Monitor memory usage when serving hundreds of large cluster snapshots simultaneously.

How do I identify which snapshot I'm querying?

The URL path contains the snapshot identifier. The first path segment after the host (e.g., /production-2024-01/) corresponds to the subdirectory name containing that snapshot's version.yaml. According to src/gather/server.rs, the handler extracts this segment to look up the correct ArchiveReader in the state map.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →