docs/sdk/python/snapshots.mdx
Capture the disk state of a stopped sandbox as a reusable artifact, then boot fresh sandboxes from it. Snapshots are disk-only and require a sandbox that is not running. See Snapshots for concepts and walkthroughs.
<div className="msb-glance"> <p className="msb-gl"><span className="msb-dot static"></span>Take a snapshot<span className="msb-ct">3</span></p> <a className="msb-row" href="#handle-snapshot"><span className="msb-rn">handle.snapshot()</span><span className="msb-rg">snapshot under a name</span></a> <a className="msb-row" href="#handle-snapshot_to"><span className="msb-rn">handle.snapshot_to()</span><span className="msb-rg">snapshot to a path</span></a> <a className="msb-row" href="#snapshot-create"><span className="msb-rn">Snapshot.create()</span><span className="msb-rg">snapshot a stopped sandbox by name</span></a> <p className="msb-gl"><span className="msb-dot static"></span>Boot from a snapshot<span className="msb-ct">1</span></p> <a className="msb-row" href="#sandbox-create"><span className="msb-rn">Sandbox.create(snapshot=...)</span><span className="msb-rg">boot a fresh sandbox from an artifact</span></a> <p className="msb-gl"><span className="msb-dot static"></span>Manage artifacts<span className="msb-ct">7</span></p> <a className="msb-row" href="#snapshot-open"><span className="msb-rn">Snapshot.open()</span><span className="msb-rg">open an artifact by name or path</span></a> <a className="msb-row" href="#snapshot-get"><span className="msb-rn">Snapshot.get()</span><span className="msb-rg">handle from the local index</span></a> <a className="msb-row" href="#snapshot-list"><span className="msb-rn">Snapshot.list()</span><span className="msb-rg">indexed snapshots</span></a> <a className="msb-row" href="#snapshot-list_dir"><span className="msb-rn">Snapshot.list_dir()</span><span className="msb-rg">parse a directory of artifacts</span></a> <a className="msb-row" href="#snapshot-remove"><span className="msb-rn">Snapshot.remove()</span><span className="msb-rg">delete an artifact</span></a> <a className="msb-row" href="#snapshot-reindex"><span className="msb-rn">Snapshot.reindex()</span><span className="msb-rg">rebuild the local index</span></a> <a className="msb-row" href="#snapshot-export"><span className="msb-rn">Snapshot.export()</span><span className="msb-rg">bundle into an archive</span></a> <p className="msb-gl"><span className="msb-dot static"></span>Move artifacts<span className="msb-ct">1</span></p> <a className="msb-row" href="#snapshot-import_"><span className="msb-rn">Snapshot.import_()</span><span className="msb-rg">unpack an archive</span></a> <p className="msb-gl"><span className="msb-dot instance"></span>Inspect<span className="msb-ct">3</span></p> <a className="msb-row" href="#snap-verify"><span className="msb-rn">snap.verify()</span><span className="msb-rg">recompute and check integrity</span></a> <a className="msb-row" href="#handle-open"><span className="msb-rn">handle.open()</span><span className="msb-rg">load full metadata from a handle</span></a> <a className="msb-row" href="#handle-remove"><span className="msb-rn">handle.remove()</span><span className="msb-rg">delete via a handle</span></a> <p className="msb-gl"><span className="msb-dot type"></span>Types</p> <div className="msb-chiprow"> <a className="msb-typepill" href="#snapshot">Snapshot</a> <a className="msb-typepill" href="#snapshothandle">SnapshotHandle</a> </div> </div> <p className="msb-label" id="typical-flow">Typical flow</p>from microsandbox import Sandbox, Snapshot
# Run setup work, then stop the sandbox
async with await Sandbox.create("baseline", image="python:3.12") as sb:
await sb.exec("pip", ["install", "numpy"])
await sb.stop()
# Capture its disk state under a name
snap = await Snapshot.create("baseline", name="after-pip-install")
print(snap.digest)
# Boot a fresh sandbox from the artifact
sb2 = await Sandbox.create("worker", snapshot="after-pip-install")
async def snapshot(self, name: str) -> Snapshot
Snapshot this sandbox under a bare name in the default snapshots directory (~/.microsandbox/snapshots/<name>/). The sandbox must be stopped or crashed. For an explicit filesystem destination, see snapshot_to(). Called on a SandboxHandle, obtained from Sandbox.get().
handle = await Sandbox.get("baseline")
snap = await handle.snapshot("after-pip-install")
print(snap.digest)
async def snapshot_to(self, path: str | os.PathLike) -> Snapshot
Snapshot this sandbox to an explicit filesystem path. The sandbox must be stopped or crashed.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">str | os.PathLike</span></div> <div className="msb-param-desc">Destination artifact directory.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#snapshot">Snapshot</a></div> <div className="msb-param-desc">The captured snapshot.</div> </div> </div> <Accordion title="Example">handle = await Sandbox.get("baseline")
snap = await handle.snapshot_to("/data/snapshots/baseline")
@staticmethod
async def create(
source_sandbox: str,
*,
name: str | None = None,
path: str | os.PathLike | None = None,
labels: dict[str, str] | None = None,
force: bool = False,
record_integrity: bool = False,
) -> Snapshot
Create a snapshot from a stopped or crashed sandbox. Exactly one of name= (resolved under the default snapshots directory) or path= (explicit filesystem destination) is required.
snap = await Snapshot.create(
"baseline",
name="after-pip-install",
labels={"stage": "post-deps"},
record_integrity=True,
)
@staticmethod
async def create(name: str, *, snapshot: str | os.PathLike | None = None, **kwargs) -> Sandbox
Boot a fresh sandbox from a snapshot artifact by passing snapshot= as a peer of image=. The two are mutually exclusive: pass exactly one. See Sandbox.create() for the full set of configuration kwargs.
# Boot from a snapshot
sb = await Sandbox.create("worker", snapshot="after-pip-install")
# Or from an image (existing flow, unchanged)
sb = await Sandbox.create("worker", image="python:3.12")
@staticmethod
async def open(path_or_name: str) -> Snapshot
Open an existing artifact by bare name (resolved under the default snapshots directory) or path. Cheap metadata validation only; does not read the upper file. Use verify() for content checks.
snap = await Snapshot.open("after-pip-install")
print(snap.image_ref)
@staticmethod
async def get(name_or_digest: str) -> SnapshotHandle
Look up a handle in the local index by name, digest, or path.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name_or_digest</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Snapshot name, digest, or path.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#snapshothandle">SnapshotHandle</a></div> <div className="msb-param-desc">Lightweight handle backed by an index row.</div> </div> </div> <Accordion title="Example">h = await Snapshot.get("after-pip-install")
print(h.digest)
@staticmethod
async def list() -> list[SnapshotHandle]
List indexed snapshots from the local DB cache.
<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#snapshothandle">list[SnapshotHandle]</a></div> <div className="msb-param-desc">Indexed snapshot handles.</div> </div> </div> <Accordion title="Example">for h in await Snapshot.list():
print(h.name, h.digest)
@staticmethod
async def list_dir(dir: str | os.PathLike) -> list[Snapshot]
Walk a directory and parse each subdirectory's manifest. Does not touch the index, useful for inspecting external snapshot collections (e.g. a mounted volume of artifacts that were never imported). Skips entries that don't look like snapshot artifacts.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>dir</code><span className="msb-type">str | os.PathLike</span></div> <div className="msb-param-desc">Directory to scan for artifacts.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#snapshot">list[Snapshot]</a></div> <div className="msb-param-desc">One snapshot per valid artifact directory.</div> </div> </div> <Accordion title="Example">for snap in await Snapshot.list_dir("/mnt/artifacts"):
print(snap.path, snap.digest)
@staticmethod
async def remove(path_or_name: str, *, force: bool = False) -> None
Remove a snapshot artifact and its index row. Refuses if the snapshot has indexed children unless force=True.
await Snapshot.remove("after-pip-install", force=True)
@staticmethod
async def reindex(dir: str | os.PathLike | None = None) -> int
Walk dir (default: configured snapshots dir) and rebuild the local index. Returns the number of artifacts indexed.
count = await Snapshot.reindex()
print(f"indexed {count} snapshots")
@staticmethod
async def export(
name_or_path: str,
out: str | os.PathLike,
*,
with_parents: bool = False,
with_image: bool = False,
plain_tar: bool = False,
) -> None
Bundle a snapshot into a .tar.zst archive. The existing snapshot manifest is archived as-is; create the snapshot with recorded integrity when the archive will cross a trust boundary.
await Snapshot.export(
"after-pip-install",
"/tmp/after-pip-install.tar.zst",
with_parents=True,
)
@staticmethod
async def import_(
archive: str | os.PathLike,
*,
dest: str | os.PathLike | None = None,
) -> SnapshotHandle
Unpack a snapshot archive (.tar.zst or .tar) into the snapshots directory, verifying recorded integrity when present. Compression is detected from magic bytes. The trailing underscore is intentional: import is a reserved Python keyword.
h = await Snapshot.import_("/tmp/after-pip-install.tar.zst")
print(h.path)
async def verify(self) -> dict[str, Any]
Recompute the upper layer's content hash and compare against the manifest. Walks data extents only, so a 4 GiB sparse file with a few MB of data verifies in milliseconds.
<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">dict[str, Any]</span></div> <div className="msb-param-desc">Verification report. The <code>upper.kind</code> field is <code>"not_recorded"</code> when no integrity hash was stored, or <code>"verified"</code> with the recomputed digest.</div> </div> </div> <Accordion title="Example">report = await snap.verify()
if report["upper"]["kind"] == "verified":
print(f"hash matches: {report['upper']['digest']}")
else:
print("no integrity hash recorded")
The report shape:
{
"digest": "sha256:...",
"path": "/path/to/artifact",
"upper": {"kind": "not_recorded"} # no integrity recorded
| {"kind": "verified", "algorithm": "...", "digest": "sha256:..."},
}
async def open(self) -> Snapshot
Load the full Snapshot metadata for this handle. Metadata-validated only; does not read the upper file.
h = await Snapshot.get("after-pip-install")
snap = await h.open()
print(snap.fstype)
async def remove(self, *, force: bool = False) -> None
Remove this snapshot artifact and its index row. Refuses if the snapshot has indexed children unless force=True.
h = await Snapshot.get("after-pip-install")
await h.remove(force=False)
A fully-parsed snapshot artifact. Properties are read-only attributes (not async).
| Property / Method | Type | Description |
|---|---|---|
path | str | Path to the artifact directory |
digest | str | Canonical content digest (sha256:hex). The snapshot's identity |
size_bytes | int | Apparent size of the captured upper layer in bytes (sparse on disk) |
image_ref | str | Image reference the snapshot was taken from |
image_manifest_digest | str | OCI manifest digest of the pinned image |
format | str | "raw" or "qcow2" (always "raw" today) |
fstype | str | Filesystem type inside the upper (e.g. "ext4") |
parent | str | None | Parent snapshot's digest, or None for a root |
created_at | str | RFC 3339 timestamp |
labels | dict[str, str] | User-supplied labels |
source_sandbox | str | None | Best-effort source-sandbox name |
verify() | Awaitable[dict[str, Any]] | Recompute and check the upper-layer integrity hash. See verify() |
Lightweight handle backed by an index row. Properties are read-only attributes (not async).
| Property / Method | Type | Description |
|---|---|---|
digest | str | Manifest digest, canonical identity |
name | str | None | Convenience alias |
parent_digest | str | None | Parent snapshot digest, or None for a root |
image_ref | str | Image the snapshot was taken from |
format | str | "raw" or "qcow2" |
size_bytes | int | None | Apparent upper size at index time |
created_at | float | ms since Unix epoch |
path | str | Local artifact directory path |
open() | Awaitable[Snapshot] | Load full metadata. See open() |
remove(force=False) | Awaitable[None] | Delete the artifact and its index row. See remove() |