Back to Microsandbox

Snapshots

docs/sandboxes/snapshots.mdx

0.6.79.3 KB
Original Source

A snapshot is a portable, on-disk capture of a sandbox's writable filesystem. Move it with scp, archive it as .tar.zst, or boot fresh sandboxes from it.

<Note> Snapshots are **disk-only** and require a sandbox that is not running. Stopped and crashed sandboxes can be snapshotted; running, draining, and paused sandboxes are rejected. </Note>

What gets captured

CapturedNot captured
Writable filesystem changesMemory contents
Pinned image identityRunning processes
Optional labels and integrity hashNetwork state

Booting from a snapshot is a cold boot of a fresh VM that starts from the captured filesystem changes.

Quick start

You'll usually reach for the CLI first:

bash
# 1. Boot a sandbox, install state, then stop it
msb run --name baseline --detach python:3.12
msb exec baseline -- pip install requests
msb stop baseline

# 2. Snapshot the stopped sandbox
msb snapshot create after-pip-install --from baseline

# 3. Boot a fresh sandbox from the snapshot
msb run --name worker --from-snapshot after-pip-install \
    -- python -c "import requests; print(requests.__version__)"
<Tip> By default, the snapshot lives at `~/.microsandbox/snapshots/after-pip-install/`. That whole directory is the snapshot. </Tip>

Snapshot a sandbox

Snapshot under a bare name, resolved to ~/.microsandbox/snapshots/<name>/ by default. The name is the snapshot's identity; pass a destination directory to create the artifact on a different volume (DIR/<name>). Either way the directory is the whole artifact; move it with save/load (or plain mv):

<CodeGroup> ```rust Rust use microsandbox::Sandbox;

let h = Sandbox::get("baseline").await?;

// Resolves under ~/.microsandbox/snapshots/<name>/ let snap = h.snapshot("after-pip-install").await?;

println!("{}", snap.digest()); // sha256:...


```typescript TypeScript
import { Sandbox } from "microsandbox";

const h = await Sandbox.get("baseline");

// Resolves under ~/.microsandbox/snapshots/<name>/
const snap = await h.snapshot("after-pip-install");

console.log(snap.digest); // sha256:...
python
from microsandbox import Sandbox

h = await Sandbox.get("baseline")

# Resolves under ~/.microsandbox/snapshots/<name>/
snap = await h.snapshot("after-pip-install")

print(snap.digest)  # sha256:...
go
h, err := m.GetSandbox(ctx, "baseline")
if err != nil {
    return err
}

// Resolves under ~/.microsandbox/snapshots/<name>/
snap, err := h.Snapshot(ctx, "after-pip-install")

fmt.Println(snap.Digest()) // sha256:...
bash
msb snapshot create after-pip-install --from baseline
msb snapshot create after-pip-install --from baseline --label stage=ready

# Create the artifact on another volume: lands at /mnt/big/after-pip-install
msb snapshot create after-pip-install --from baseline --dest-dir /mnt/big
</CodeGroup>

The sandbox must be stopped or crashed; running sandboxes are rejected.

Boot from a snapshot

A snapshot already pins its image, so booting from one is mutually exclusive with the image source:

<CodeGroup> ```rust Rust use microsandbox::Sandbox;

let sb = Sandbox::builder("worker") .from_snapshot("after-pip-install") .create() .await?;


```typescript TypeScript
import { Sandbox } from "microsandbox";

const sb = await Sandbox.builder("worker")
    .fromSnapshot("after-pip-install")
    .create();
python
from microsandbox import Sandbox

# `from_snapshot=` is a peer of `image=` and mutually exclusive with it
sb = await Sandbox.create("worker", from_snapshot="after-pip-install")
go
sb, err := m.CreateSandbox(ctx, "worker",
    m.WithFromSnapshot("after-pip-install"),
)
bash
msb run --name worker --from-snapshot after-pip-install -- python -V
</CodeGroup>

Booting validates the snapshot, resolves the pinned image, and gives the new sandbox its own writable copy.

List, inspect, and remove

<CodeGroup> ```rust Rust use microsandbox::Snapshot;

let all = Snapshot::list().await?; // Indexed snapshots let h = Snapshot::get("after-pip-install").await?; // By name, digest, or path println!("{} ({})", h.name().unwrap_or("-"), h.digest());

Snapshot::remove("after-pip-install", false).await?; Snapshot::reindex("/data/snapshots").await?;


```typescript TypeScript
import { Snapshot } from "microsandbox";

const all = await Snapshot.list();                       // Indexed snapshots
const h = await Snapshot.get("after-pip-install");       // By name, digest, or path
console.log(`${h.name ?? "-"} (${h.digest})`);

await Snapshot.remove("after-pip-install");
await Snapshot.reindex();                               // Default snapshots directory
python
from microsandbox import Snapshot

all = await Snapshot.list()                              # Indexed snapshots
h = await Snapshot.get("after-pip-install")              # By name, digest, or path
print(f"{h.name or '-'} ({h.digest})")

await Snapshot.remove("after-pip-install")
await Snapshot.reindex()                                 # Default snapshots directory
go
all, err := m.Snapshot.List(ctx)            // Indexed snapshots
fmt.Printf("%d snapshots\n", len(all))
h, err := m.Snapshot.Get(ctx, "after-pip-install") // By name, digest, or path
name := "-"
if h.Name() != nil {
    name = *h.Name()
}
fmt.Printf("%s (%s)\n", name, h.Digest())

err = m.Snapshot.Remove(ctx, "after-pip-install", false)
_, err = m.Snapshot.Reindex(ctx, "/data/snapshots")
bash
msb snapshot ls
msb snapshot inspect after-pip-install
msb snapshot rm after-pip-install

# Also if it has indexed children
msb snapshot rm after-pip-install --force

# Rebuild the index from artifacts on disk
msb snapshot reindex
</CodeGroup>

list and get use a local index for fast lookup. If the index gets out of sync, reindex rebuilds it from the snapshot artifacts on disk.

Move snapshots between machines

The snapshot directory is the whole artifact; there is no hidden daemon state. Copy the directory directly, or save it as an archive:

bash
# Copy the directory directly with scp (image must be cached or pullable on the target)
scp -r ~/.microsandbox/snapshots/after-pip-install \
    other-host:~/.microsandbox/snapshots/

# Bundle into a .tar.zst, transport, then load
msb snapshot save after-pip-install /tmp/snap.tar.zst
scp /tmp/snap.tar.zst other-host:
ssh other-host msb snapshot load /tmp/snap.tar.zst

# Fully offline: include the OCI image cache so the target needs no network
msb snapshot save after-pip-install /tmp/snap.tar.zst --with-image
ssh other-host msb snapshot load /tmp/snap.tar.zst

Archives default to .tar.zst. Pass --plain-tar for a plain .tar. SDKs expose the same save and load operations as the CLI.

Integrity verification

By default, snapshot creation records enough metadata to validate the artifact without hashing the full writable layer. Opt in to a content-integrity hash when crossing a trust boundary:

<CodeGroup> ```rust Rust use microsandbox::Snapshot;

// Compute and record an integrity hash at create time let snap = Snapshot::builder("after-pip-install") .from_sandbox("baseline") .record_integrity() .create() .await?;

// Verify a snapshot's recorded integrity on demand let report = snap.verify().await?;


```typescript TypeScript
import { Snapshot } from "microsandbox";

// Compute and record an integrity hash at create time
const snap = await Snapshot.builder("after-pip-install")
  .fromSandbox("baseline")
  .recordIntegrity()
  .create();

// Verify a snapshot's recorded integrity on demand
const report = await snap.verify();
python
from microsandbox import Snapshot

# Compute and record an integrity hash at create time
snap = await Snapshot.create(
    "after-pip-install",
    from_sandbox="baseline",
    record_integrity=True,
)

# Verify a snapshot's recorded integrity on demand
report = await snap.verify()
go
// Compute and record an integrity hash at create time
snap, err := m.Snapshot.Create(ctx,
    m.SnapshotCreateOptions{
        Name:            "after-pip-install",
        FromSandbox:     "baseline",
        RecordIntegrity: true,
    },
)

// Verify a snapshot's recorded integrity on demand
report, err := snap.Verify(ctx)
bash
# Compute and record an integrity hash at create time
msb snapshot create after-pip-install --from baseline --integrity

# Verify a snapshot's recorded integrity on demand
msb snapshot verify after-pip-install
msb snapshot inspect after-pip-install --verify
</CodeGroup>

msb snapshot save bundles the snapshot as it exists on disk. msb snapshot load verifies recorded integrity when the archive includes it, so create snapshots with --integrity before saving across a trust boundary.

Use cases

  • Reusable build state. Install dependencies once, snapshot, then msb run --from-snapshot ... repeatedly without paying the install cost. Common pattern for CI, agent workloads, and reproducible dev environments.
  • Portable scratch state. Capture a sandbox after a long setup, hand the artifact to a teammate or push it to shared storage, and let them boot from the same starting point.
  • Local fork-by-copy. Multiple sandboxes from one snapshot are independent; each copy of the upper layer diverges on its own.
  • Disaster recovery. Snapshot a sandbox before a risky migration; if it goes wrong, msb rm the broken one and msb run --from-snapshot from the pre-migration artifact.