SANDBOX/Snapshot And Restore

#Snapshot And Restore

Sandbox rootfs snapshots are point-in-time copies of a paused sandbox writable root filesystem. Use them when you want to checkpoint an initialized workspace, claim new sandboxes from that known filesystem state, roll an existing sandbox back, or fork a paused sandbox into an isolated child sandbox.

Use this page when you need to:

  • create named rootfs snapshots from a paused sandbox
  • claim a new sandbox with a named rootfs snapshot
  • restore a paused sandbox rootfs from a snapshot
  • fork a paused sandbox into another paused sandbox
  • understand how rootfs snapshots differ from pause/resume and Volumes

Creating snapshots, restoring, and forking require a paused sandbox. Claiming with snapshot_id creates a new running sandbox from an existing snapshot and does not require a paused target sandbox. If you call paused-only operations while the sandbox is running, Sandbox0 returns 409 Conflict.

How It Differs From Pause And Resume#

CapabilityPurposeCreates a named restore pointCreates another sandboxRequires paused sandbox
Pause/resumeRelease compute and later continue the same sandbox identity from its latest checkpointNoNoPause starts from a running sandbox; resume starts from a paused sandbox
SnapshotPublish an immutable point-in-time rootfs recordYesNoYes
Claim with snapshot_idCreate a running sandbox initialized from a named snapshotUses an existing snapshotYesNo target sandbox required
RestoreMove a paused sandbox rootfs back to a snapshotUses an existing snapshotNoYes
ForkCreate an isolated paused sandbox from a paused source sandbox rootfsNoYesYes

Pause/resume keeps the latest checkpoint for one sandbox identity. Rootfs snapshots are explicit records that you can list, fetch, claim from, restore, and delete. Fork creates a new sandbox with Copy-on-Write rootfs isolation from the source rootfs state.

Use Volumes when data must be mounted by multiple sandboxes, accessed directly through the Volume file API, or managed independently from sandbox rootfs lifecycle. Use sandbox rootfs snapshots when the default writable filesystem is the state you want to version.

State Model#

  • Create snapshot reads the paused sandbox's current rootfs head and creates a snapshot record.
  • Claim with snapshot_id creates a running sandbox and initializes its writable rootfs from the selected snapshot before process APIs are initialized.
  • Restore changes a paused target sandbox rootfs head to the selected snapshot and leaves the sandbox paused.
  • Fork creates a new paused sandbox with the source sandbox configuration and an isolated rootfs fork.
  • Resume is required before reading or writing files through sandbox process and file APIs after restore or fork. A sandbox claimed with snapshot_id starts running.
  • Deleting a snapshot does not modify any sandbox already claimed or restored from it, and it does not affect forks created from the same rootfs state.

The rootfs snapshot API stores snapshot metadata including name, description, and optional expires_at. Delete snapshots explicitly when cleanup workflows no longer need them.

Initialize Once, Claim Many#

Rootfs snapshots plus claim-time snapshot_id can act like a lightweight custom rootfs when your changes are ordinary filesystem state. Instead of building a custom image and maintaining a team-owned warm pool, start from a builtin template, initialize the sandbox, publish a rootfs snapshot, then claim new sandboxes from that snapshot.

Use this pattern for:

  • package installs and dependency caches
  • cloned repositories and checked-out branches
  • generated build artifacts that future runs should inherit
  • local tool configuration written into the sandbox filesystem

Typical workflow:

  1. claim a sandbox from a builtin template backed by the platform warm pool
  2. install dependencies or prepare the workspace inside the sandbox rootfs
  3. pause the sandbox and wait until status is paused
  4. create a named rootfs snapshot for the initialized state
  5. claim each task sandbox with the same template and snapshot_id
bash
SEED_SANDBOX_ID="$(s0 -o json sandbox create --template default --hard-ttl 86400 | jq -r '.id')" # Initialize the rootfs with your normal sandbox commands, file writes, or SSH session. s0 sandbox pause "$SEED_SANDBOX_ID" # Wait until this shows status "paused". s0 sandbox get "$SEED_SANDBOX_ID" SNAPSHOT_ID="$(s0 -o json sandbox snapshot create "$SEED_SANDBOX_ID" \ --name python-deps-v1 \ --description "Default template after Python dependency install" \ | jq -r '.id')" TASK_SANDBOX_ID="$(s0 -o json sandbox create \ --template default \ --snapshot-id "$SNAPSHOT_ID" \ --hard-ttl 3600 \ | jq -r '.id')"

Claiming with snapshot_id is the preferred path for reusable custom rootfs state because the result is a fresh running sandbox that still uses the builtin template's image, resources, mounts, services, and network defaults. Use restore when you need to roll an existing paused sandbox back to a snapshot. Use fork when you specifically need a paused child sandbox cloned from a paused seed.

For a longer walkthrough of this pattern, see Initialize Once, Claim Many.

This pattern does not replace template-level configuration. Use a custom template when you need a different container image, resource limits, mount declarations, default network policy, environment defaults, or privileged runtime fields. Mounted Sandbox Volumes are separate from the sandbox rootfs and should be managed with Volume snapshot and fork APIs.

Pause Before Rootfs Operations#

Pause the sandbox and wait until status is paused before creating a snapshot, restoring, or forking.

POST

/api/v1/sandboxes/{id}/pause

go
_, err := client.PauseSandbox(ctx, sandbox.ID) if err != nil { log.Fatal(err) } for { sb, err := client.GetSandbox(ctx, sandbox.ID) if err != nil { log.Fatal(err) } if sb.Status == apispec.SandboxLifecycleStatusPaused { break } time.Sleep(time.Second) }

Create A Snapshot#

Create a rootfs snapshot from a paused sandbox.

POST

/api/v1/sandboxes/{id}/snapshots

Request Body#

FieldTypeDescription
namestringOptional snapshot name
descriptionstringOptional snapshot description
expires_atstringOptional RFC3339 timestamp stored as snapshot expiration metadata
go
snapshot, err := client.CreateSandboxRootFSSnapshot(ctx, sandbox.ID, &apispec.CreateSandboxRootFSSnapshotRequest{ Name: apispec.NewOptString("before-agent-task"), Description: apispec.NewOptString("Workspace after dependency install"), }) if err != nil { log.Fatal(err) } fmt.Printf("Snapshot ID: %s\n", snapshot.ID)

List And Get Snapshots#

List snapshots created from a sandbox, or fetch a snapshot directly by snapshot ID.

GET

/api/v1/sandboxes/{id}/snapshots

GET

/api/v1/sandbox-rootfs-snapshots/{snapshot_id}

go
snapshots, err := client.ListSandboxRootFSSnapshots(ctx, sandbox.ID) if err != nil { log.Fatal(err) } for _, item := range snapshots.Snapshots { name := item.Name.Or("") fmt.Printf("%s %s\n", item.ID, name) } snapshot, err = client.GetSandboxRootFSSnapshot(ctx, snapshot.ID) if err != nil { log.Fatal(err) } fmt.Printf("Created at: %s\n", snapshot.CreatedAt)

Claim From A Snapshot#

Use snapshot_id on sandbox claim when you want a new running sandbox initialized from a named rootfs snapshot. The requested template still controls image, resources, mount declarations, services, network defaults, and other runtime shape. Sandbox0 applies the snapshot rootfs before file APIs, contexts, services, and SSH are initialized.

POST

/api/v1/sandboxes

Request Body#

FieldTypeDescription
templatestringTemplate ID to use
snapshot_idstringRootfs snapshot ID used to initialize the new sandbox writable rootfs
configobjectOptional sandbox configuration
mountsarrayOptional claim-time Sandbox Volume mounts
go
sandbox, err := client.ClaimSandbox(ctx, "default", sandbox0.WithSandboxSnapshotID(snapshot.ID), sandbox0.WithSandboxHardTTL(3600), ) if err != nil { log.Fatal(err) } fmt.Printf("Sandbox ID: %s\n", sandbox.ID)

Restore A Sandbox Rootfs#

Restore moves a paused target sandbox rootfs to the selected snapshot and returns the sandbox status. The sandbox stays paused after restore; resume it before using files, contexts, services, or SSH.

POST

/api/v1/sandboxes/{id}/rootfs/restore

Request Body#

FieldTypeDescription
snapshot_idstringRootfs snapshot ID to restore

Restore is destructive for the target sandbox rootfs. Files changed after the restored snapshot are no longer the target sandbox head after restore.

go
restored, err := client.RestoreSandboxRootFS(ctx, sandbox.ID, apispec.RestoreSandboxRootFSRequest{ SnapshotID: snapshot.ID, }) if err != nil { log.Fatal(err) } fmt.Printf("Restored snapshot %s, status=%s\n", restored.SnapshotID, restored.Status) _, err = client.ResumeSandbox(ctx, sandbox.ID) if err != nil { log.Fatal(err) }

Fork A Sandbox#

Fork creates a new paused sandbox from a paused source sandbox rootfs. The fork receives its own sandbox ID. Writes made after resuming the fork do not modify the source sandbox rootfs.

POST

/api/v1/sandboxes/{id}/fork

go
forked, err := client.ForkSandbox(ctx, sandbox.ID, nil) if err != nil { log.Fatal(err) } fmt.Printf("Forked sandbox: %s\n", forked.Sandbox.ID) _, err = client.ResumeSandbox(ctx, forked.Sandbox.ID) if err != nil { log.Fatal(err) }

Delete A Snapshot#

Delete a rootfs snapshot when you no longer need it. This does not modify any sandbox that already restored from that snapshot, and it does not affect forks created from the same rootfs state.

DELETE

/api/v1/sandbox-rootfs-snapshots/{snapshot_id}

go
_, err := client.DeleteSandboxRootFSSnapshot(ctx, snapshot.ID) if err != nil { log.Fatal(err) } fmt.Println("Snapshot deleted")

Typical Pattern#

For coding-agent and test-generation workflows, a common pattern is:

  1. claim a sandbox and initialize the repository or dependency cache
  2. write a marker, lockfile, or workspace state that future tasks should inherit
  3. pause the sandbox and wait for status to become paused
  4. create a rootfs snapshot for that initialized state
  5. claim each task sandbox with the same template and snapshot_id
  6. run task-specific work in the newly claimed sandbox

Next Steps#

Pause And Resume

Control the lifecycle state required before snapshot, restore, and fork operations.

Files

Read and write sandbox files before creating a rootfs snapshot.

Volumes

Use durable shared storage when rootfs snapshots are not the right persistence boundary.

Initialize Once, Claim Many

Use rootfs snapshots and claim-time snapshot IDs as lightweight custom rootfs state.