Snapshots
Capturing and restoring a running microVM's state
client.snapshots captures and restores the state of a running microVM. Take
a snapshot before something risky, and restore if it goes wrong:
snap = client.snapshots.create(
env.id, fuse.SnapshotRequest(comment="before risky migration", mode="manual")
)
# ... do the risky thing ...
client.snapshots.restore(snap.id)
create’svm_idmust be arunningenvironment.requestis optional, omit it for a plain snapshot with no metadata;modeis"manual"or"auto", andretention_secondsof0keeps it forever.restoreis in place only: it restores onto the snapshot’s original VM, which must still be running. There is no cross-host restore, clone, or standalone restore into a new VM, useforkon the environments service for that instead.
Live snapshots
SnapshotRequest.live opts into capturing the guest’s memory and vCPU state
alongside the disk. It defaults to False, a disk-only snapshot, which is the
default everywhere and the fallback:
snap = client.snapshots.create(
env.id, fuse.SnapshotRequest(comment="warmed model loaded", live=True)
)
# snap.kind is "live" here, "disk" on every other snapshot
Snapshot.kind is "live" or "disk" and is set by the host agent, not by the
caller reading its own request back. restore has no live counterpart: it
reads the kind off the snapshot and either resumes the guest or cold-boots it.
The trade-offs are the same ones documented under Snapshots, and they are sharp enough to repeat:
Snapshot.size_byteson a live snapshot includes a full copy of the environment’s configured memory, on every create. Tenant byte quotas were calibrated for rootfs-only snapshots, so acreateloop withlive=Truewill hit the byte quota much sooner than the snapshot count implies.- the guest is paused for the duration of the capture.
- a live snapshot cannot seed a new environment.
forkandCreateRequest.seed_snapshot_idread only a snapshot’s rootfs, and a live snapshot’s rootfs is captured without quiescing the guest filesystem, so it mounts cleanly only alongside its memory image. Both reject one with409conflict. Take a disk snapshot when you want a copy rather than a rewind. - a live snapshot is pinned to the host that took it. The memory image depends on that host’s CPU model and Firecracker version.
live=True against a backend with no live-snapshot support raises
fuse.ApiError with status == 501 and code == "unimplemented", not a
conflict, so fuse.is_conflict(err) reports False for it. The message says to
retry without live for a disk snapshot.
Snapshot.kind is worth checking rather than assuming: a host running an agent
too old to know about live snapshots answers live=True with a disk snapshot,
and the record then says "disk". Recording the request rather than the result
would file a cold-booting artifact as a resumable one.
Creating or restoring a snapshot against a VM in any state other than
running raises fuse.ApiError with status == 409 and code == "conflict",
whose message names the offending state. The two paths word it differently:
create includes vm vm-abc in state destroyed: snapshots require running,
restore includes vm vm-abc in state destroyed: restore requires running. Use
fuse.is_conflict(err) to branch on it rather than matching the message.
Everything else
| Method | Purpose |
|---|---|
list(vm_id=..., task_id=..., tenant_id=..., state=...) |
List snapshots, all filters optional. |
get(snapshot_id) |
Fetch one snapshot’s full detail, including state (creating|ready|restoring|deleting|error), kind (disk|live), and exports. |
delete(snapshot_id) |
Leaf-only delete; also removes the underlying provider artifact. No soft-delete. |
These mirror the HTTP API one to one.
The GPU exception
Any environment that requested spec.gpus > 0 cannot be snapshotted or forked
at all, whether those GPUs are whole devices or MIG instances, because the
qemu backend deliberately omits snapshot support for VFIO passthrough. The two
paths reach that outcome differently: fork checks the requested GPU count up
front, before the provider is consulted, whereas snapshot is gated by the
backend, and the GPU count only selects which error message you get once the
backend has already declined. See
Providers.
A zero-GPU environment that landed on a qemu host also cannot be snapshotted,
but it surfaces the generic provider-unsupported error (provider does not support snapshots for vm ...) rather than the GPU one.
The GPU guardrail raises fuse.ApiError with status == 409 and
code == "conflict", so fuse.is_conflict(err) reports True. The generic
provider-unsupported case is distinct: it raises status == 501 and
code == "unimplemented". Both are permanent, so neither should be retried.