> ## Documentation Index
> Fetch the complete documentation index at: https://docs.celesto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Snapshot and restore SmolVM sandboxes

> Snapshot and restore running SmolVM sandboxes to checkpoint agents, preserve configured environments, retry failed steps, and skip cold-start setup.

Snapshots let you save a sandbox and bring it back later. This is useful when you want to keep a configured environment, retry a failed step, or checkpoint an agent's progress before a risky operation.

A full snapshot captures the VM's CPU state, memory, and disk. When you restore it, the sandbox resumes where it was when you created the snapshot.

<Tip>
  If you only need a workspace folder that an agent can reopen across runs, use [SmolFS](/smolfs/overview). Snapshots are best when you need the whole sandbox state.
</Tip>

<Note>
  Snapshots are available on Firecracker and QEMU for Linux guests that use isolated disks. Windows guests, workspace mounts, and extra drives are not supported yet. QEMU snapshots require a qcow2 per-VM disk, so raw QEMU disks created for `grow_filesystem=True` cannot be snapshotted.
</Note>

## When to use snapshots

* **Checkpoint before risky operations** — Save state before an agent runs untrusted code, so you can roll back if something goes wrong.
* **Reuse a configured environment** — Set up a sandbox with the right packages and files, snapshot it, and restore it multiple times instead of repeating setup.
* **Speed up agent workflows** — Skip boot and configuration time by restoring from a ready-to-go snapshot.

## Create a snapshot

To create a snapshot, the VM must be running or paused. SmolVM pauses the VM during snapshot creation and optionally resumes it afterward.

<Tabs>
  <Tab title="Python SDK">
    ```python theme={null} theme={null}
    from smolvm import SmolVM

    vm = SmolVM()
    vm.start()

    # Install packages and configure the environment
    vm.run("apk add python3 py3-pip")
    vm.run("pip install requests")

    # Save the current state
    snapshot = vm.snapshot(snapshot_id="my-checkpoint")
    print(f"Snapshot created: {snapshot.snapshot_id}")

    vm.close()
    ```

    By default, the VM stays paused after a snapshot. Pass `resume_source=True` to keep working with it:

    ```python theme={null} theme={null}
    snapshot = vm.snapshot(
        snapshot_id="my-checkpoint",
        resume_source=True,
    )
    # VM is still running — you can continue using it
    result = vm.run("echo 'still running'")
    ```

    If you omit `snapshot_id`, SmolVM generates one automatically (for example, `snap-my-vm-1717012345`).
  </Tab>

  <Tab title="CLI">
    ```bash theme={null} theme={null}
    smolvm sandbox snapshot create my-vm --snapshot-id my-checkpoint
    ```

    To keep the source VM running after the snapshot:

    ```bash theme={null} theme={null}
    smolvm sandbox snapshot create my-vm --snapshot-id my-checkpoint --resume-source
    ```

    If you skip `--snapshot-id`, SmolVM generates a unique name automatically.
  </Tab>
</Tabs>

## Choose a snapshot type

When you create a snapshot, you can pick how much of the VM to store:

* **`full`** (default) — Saves a complete, self-contained disk copy plus the guest's memory and CPU state. Use this when you want to resume the sandbox exactly where you left off.
* **`diff`** — Saves a smaller disk artifact. On QEMU, the snapshot state is stored inside the qcow2 artifact and the backing image must still exist at restore time. On Firecracker, SmolVM still captures memory and VM state in full, and uses a copy-on-write disk copy when the host filesystem supports it.
* **`disk`** — Saves only the disk and skips the memory dump. Restoring boots the sandbox fresh from that disk instead of resuming the exact running process state. Use this when you only need filesystem state and a cold boot is acceptable.

On Firecracker, `full` and `diff` snapshots capture VM state and memory. `disk` snapshots copy only the managed disk, so they restore with a fresh boot.

<Note>
  `disk` snapshots are much faster and use less space than `full` because they skip the RAM dump. They are the right choice for workflows where you only need the filesystem state.
</Note>

<Tabs>
  <Tab title="Python SDK">
    ```python theme={null} theme={null}
    from smolvm import SmolVM

    vm = SmolVM()
    vm.start()

    # Space-saving snapshot — stores only what changed since the base image
    snapshot = vm.snapshot(
        snapshot_id="nightly-checkpoint",
        snapshot_type="diff",
    )
    print(snapshot.snapshot_type)  # SnapshotType.DIFF

    vm.close()
    ```

    You can also pass the `SnapshotType` enum for type safety:

    ```python theme={null} theme={null}
    from smolvm import SnapshotType

    snapshot = vm.snapshot(snapshot_type=SnapshotType.DIFF)
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null} theme={null}
    # Space-saving snapshot
    smolvm sandbox snapshot create my-vm --snapshot-type diff

    # QEMU disk-only snapshot — fast, no RAM dump, restores as a cold boot
    smolvm sandbox snapshot create my-vm --snapshot-type disk

    # Full snapshot (default — same as omitting the flag)
    smolvm sandbox snapshot create my-vm --snapshot-type full
    ```
  </Tab>
</Tabs>

Take a QEMU `disk` snapshot when you want to save the filesystem state quickly and don't need to resume the running process state:

```python theme={null} theme={null}
from smolvm import SmolVM, SnapshotType

vm = SmolVM(backend="qemu")
vm.start()
vm.run("apk add python3")

# Fast, lightweight snapshot — disk only, no RAM
snapshot = vm.snapshot(
    snapshot_id="cold-boot-checkpoint",
    snapshot_type=SnapshotType.DISK,
)

# Later — restoring boots the sandbox fresh from the saved disk
vm = SmolVM.from_snapshot("cold-boot-checkpoint", resume_vm=True)
result = vm.run("python3 --version")  # python3 is already installed
```

<Note>
  If the backing image for a QEMU diff snapshot is missing at restore time, SmolVM raises a clear error pointing to the missing path and suggesting you take a `full` snapshot instead. To stay safe, keep backing images in place while their diff snapshots exist.
</Note>

## Keep a running QEMU sandbox available

By default, snapshot creation may briefly pause a running sandbox while SmolVM captures state. For QEMU disk snapshots, live capture keeps the guest running throughout — SmolVM copies the disk in the background without stopping the VM. If the installed QEMU cannot do a live block backup, the command fails rather than silently falling back to a pause.

Use live capture when a sandbox must stay reachable during the snapshot — for example, a long-running agent or an interactive session you don't want to interrupt.

Live capture has three requirements:

* The backend must be QEMU.
* `snapshot_type` must be `disk` — memory is not captured.
* `resume_source` must be `True` (`--resume-source` on the CLI). The sandbox stays running end-to-end.

<Tabs>
  <Tab title="Python SDK">
    ```python theme={null} theme={null}
    from smolvm import SmolVM, SnapshotCapturePolicy, SnapshotType

    vm = SmolVM(backend="qemu")
    vm.start()

    snapshot = vm.snapshot(
        snapshot_id="live-checkpoint",
        snapshot_type=SnapshotType.DISK,
        resume_source=True,
        capture_policy=SnapshotCapturePolicy.LIVE_ONLY,
    )
    # vm keeps running the whole time
    vm.run("echo 'still serving requests'")
    ```

    Optional live-capture tuning:

    * `timeout_seconds` — maximum time to wait for the background copy (default `600.0`).
    * `max_bytes_per_second` — cap the backup I/O bandwidth to protect other workloads.

    ```python theme={null} theme={null}
    snapshot = vm.snapshot(
        snapshot_type=SnapshotType.DISK,
        resume_source=True,
        capture_policy=SnapshotCapturePolicy.LIVE_ONLY,
        timeout_seconds=900,
        max_bytes_per_second=50 * 1024 * 1024,  # 50 MiB/s
    )
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null} theme={null}
    smolvm sandbox snapshot create my-sandbox \
      --snapshot-id live-checkpoint \
      --snapshot-type disk \
      --resume-source \
      --live-only
    ```

    Without `--live-only`, a disk snapshot may briefly pause the guest even when `--resume-source` is set. `--resume-source` only controls the final state; `--live-only` is what avoids the pause during capture.
  </Tab>
</Tabs>

<Note>
  Live capture is crash-consistent by default: the resulting disk looks like the guest was power-cut at the capture moment. Combine it with the guest flush policy below to reduce the chance of losing in-flight writes.
</Note>

## Control the guest flush before a disk snapshot

Before a disk snapshot, SmolVM asks the guest agent to flush pending filesystem writes so the copied disk includes recent changes. `flush_policy` decides what happens if that flush fails:

* **`required`** (default) — Fail the snapshot if the flush cannot succeed. Safest for workflows that need up-to-date disk state.
* **`best-effort`** — Try to flush, but continue and take a crash-consistent snapshot if the flush fails. Useful when the guest agent may be unavailable but you still want a snapshot.
* **`skip`** — Do not attempt the flush. Fastest, and the right choice when the guest has already been quiesced or when a crash-consistent copy is acceptable.

`flush_policy` applies to all `disk` snapshots — paused or live.

<Tabs>
  <Tab title="Python SDK">
    ```python theme={null} theme={null}
    from smolvm import SmolVM, GuestFlushPolicy, SnapshotCapturePolicy, SnapshotType

    snapshot = vm.snapshot(
        snapshot_type=SnapshotType.DISK,
        resume_source=True,
        capture_policy=SnapshotCapturePolicy.LIVE_ONLY,
        flush_policy=GuestFlushPolicy.BEST_EFFORT,
    )
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null} theme={null}
    smolvm sandbox snapshot create my-sandbox \
      --snapshot-type disk \
      --resume-source \
      --live-only \
      --flush-policy best-effort
    ```
  </Tab>
</Tabs>

## How SmolVM prepares the guest

Before SmolVM captures a disk snapshot, it asks the guest to save pending filesystem changes. Recent SmolVM images use the Rust guest agent over vsock for this sync step. You can adjust this behavior with `flush_policy` (see [Control the guest flush before a disk snapshot](#control-the-guest-flush-before-a-disk-snapshot)).

The sync path matters because it happens before SmolVM creates snapshot files:

<Steps>
  <Step title="SmolVM reaches the guest">
    SmolVM uses the selected control channel. On recent Linux images, this is usually vsock. On compatibility paths, it can be SSH.
  </Step>

  <Step title="The guest saves file changes">
    The guest agent exposes a dedicated `/sync` endpoint. Older compatibility paths may use a raw command to ask the guest to flush files.
  </Step>

  <Step title="SmolVM pauses and captures state">
    After sync succeeds, SmolVM pauses the sandbox and writes the snapshot artifacts for the selected snapshot type.
  </Step>
</Steps>

<Tip>
  If snapshot creation times out before a disk, memory, or state file appears, start by checking the control channel and guest-agent logs. The failure may be in guest sync rather than snapshot storage.
</Tip>

## Restore a snapshot

Restoring a snapshot recreates the original VM with the saved state. The restored VM starts in a paused state by default.

<Tabs>
  <Tab title="Python SDK">
    ```python theme={null} theme={null}
    from smolvm import SmolVM

    # Restore and resume the VM in one step
    vm = SmolVM.from_snapshot("my-checkpoint", resume_vm=True)
    print(f"Restored VM: {vm.vm_id}")
    print(f"Status: {vm.status}")  # VMState.RUNNING

    # The VM is back exactly where it was
    result = vm.run("python3 -c 'import requests; print(requests.__version__)'")
    print(result.output)

    vm.close()
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null} theme={null}
    smolvm sandbox snapshot restore my-checkpoint --resume
    ```
  </Tab>
</Tabs>

A snapshot can only be restored once by default. If you need to restore the same snapshot again, use the `force` flag:

<Tabs>
  <Tab title="Python SDK">
    ```python theme={null} theme={null}
    vm = SmolVM.from_snapshot("my-checkpoint", resume_vm=True, force=True)
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null} theme={null}
    smolvm sandbox snapshot restore my-checkpoint --resume --force
    ```
  </Tab>
</Tabs>

## List snapshots

<Tabs>
  <Tab title="Python SDK">
    ```python theme={null} theme={null}
    from smolvm import SmolVMManager

    with SmolVMManager() as sdk:
        snapshots = sdk.list_snapshots()
        for snap in snapshots:
            print(f"{snap.snapshot_id}  vm={snap.vm_id}  restored={snap.restored}")
    ```

    Filter by source VM:

    ```python theme={null} theme={null}
    snapshots = sdk.list_snapshots(vm_id="my-vm")
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null} theme={null}
    smolvm sandbox snapshot list
    ```

    Filter by VM:

    ```bash theme={null} theme={null}
    smolvm sandbox snapshot list --vm-id my-vm
    ```
  </Tab>
</Tabs>

## Delete a snapshot

Deleting a snapshot removes the saved state files and metadata. You cannot delete a snapshot while a restored VM from that snapshot is still running.

<Tabs>
  <Tab title="Python SDK">
    ```python theme={null} theme={null}
    from smolvm import SmolVMManager

    with SmolVMManager() as sdk:
        sdk.delete_snapshot("my-checkpoint")
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null} theme={null}
    smolvm sandbox snapshot delete my-checkpoint
    ```
  </Tab>
</Tabs>

<Warning>
  Deleting a snapshot is permanent. SmolVM removes the saved files for that snapshot, including any disk, VM state, and memory artifacts that snapshot type created.
</Warning>

## Full example: checkpoint and retry

This example shows how an agent can checkpoint a sandbox before running untrusted code, then roll back if something fails.

```python theme={null} theme={null}
from smolvm import SmolVM, SmolVMError

# Set up a sandbox
vm = SmolVM()
vm.start()
vm.run("apk add python3")

# Checkpoint before the risky step
snapshot = vm.snapshot(snapshot_id="before-experiment")
vm.close()

# Restore and try the experiment
vm = SmolVM.from_snapshot("before-experiment", resume_vm=True)
result = vm.run("python3 -c 'import this_will_fail'")

if not result.ok:
    print("Experiment failed — rolling back")
    vm.stop()
    vm.delete()

    # Restore from checkpoint and try a different approach
    vm = SmolVM.from_snapshot("before-experiment", resume_vm=True, force=True)
    result = vm.run("python3 -c 'print(42)'")
    print(result.output)  # 42

vm.close()
```

## Snapshot ID rules

Snapshot IDs must contain only lowercase letters, numbers, hyphens, and underscores. SmolVM validates this on creation and raises an error for invalid IDs.

Valid examples: `my-checkpoint`, `snap-agent-001`, `pre-deploy-v2`

## Error handling

SmolVM provides specific exceptions for snapshot operations:

```python theme={null} theme={null}
from smolvm import (
    SmolVM,
    SnapshotAlreadyExistsError,
    SnapshotNotFoundError,
    SnapshotType,
    SmolVMError,
)

try:
    snapshot = vm.snapshot(snapshot_id="my-checkpoint")
except SnapshotAlreadyExistsError:
    print("A snapshot with this ID already exists")
except SmolVMError as e:
    print(f"Snapshot failed: {e.message}")

try:
    vm = SmolVM.from_snapshot("missing-snapshot")
except SnapshotNotFoundError:
    print("Snapshot not found")
```

## Next steps

<CardGroup cols={2}>
  <Card title="Snapshot CLI reference" icon="terminal" href="/smolvm/cli/snapshot">
    Full list of snapshot CLI commands and options
  </Card>

  <Card title="SnapshotInfo API" icon="code" href="/smolvm/api/snapshotinfo">
    Snapshot metadata returned by the SDK
  </Card>

  <Card title="VM lifecycle" icon="arrows-rotate" href="/smolvm/guides/vm-lifecycle">
    Understand the full sandbox lifecycle
  </Card>

  <Card title="AI agent integration" icon="robot" href="/smolvm/guides/ai-agent-integration">
    Build secure agent sandboxes with checkpointing
  </Card>
</CardGroup>
