Cubicle

Snapshot and restore a Box

Take a disk snapshot of a stopped Cubicle Box, list its snapshots, restore the Box in place, and create a new Box from a snapshot with its own size, using the TypeScript SDK, the CLI or the HTTP API.

A snapshot is a copy of a stopped Box's whole disk: the operating system, installed software and every home directory, but no memory or running processes. Take one before a risky change so you can roll the Box back, or use one as a template for new Boxes. Snapshots are stored on the Box's Node and are deleted with their Box. Snapshots explains the rules.

Before you begin

  • Set up the SDK or the CLI as in the Quickstart. The examples use a Box acme/job-1 (cubicle boxes create --org acme job-1 --wait). TypeScript snippets use const cubicle = new Cubicle() and const box = cubicle.boxes.ref("acme/job-1"), and curl snippets read a write key from CB_SDK_API_KEY and use jq.
  • You need a grant on the Box, or the owner or admin role in its organization. An API key needs write scope to take or restore a snapshot.

Take a snapshot

Stop the Box first: if its desired state is not stopped, the request fails with 409 conflict "stop the box before taking a snapshot". A hibernated Box counts as not stopped, so stop it too, which discards its saved memory. Then wait until it has finished stopping (state is stopped at the current desired.generation), or the snapshot operation can fail with "box must be stopped". The SDK's stop() and the CLI's --wait wait for this (requirements).

Snapshot names use lowercase letters, digits, ., _ and -, start with a letter or digit, and are at most 63 characters. Names need not be unique, so refer to a snapshot by its ID.

await box.stop() // waits until the Box is stopped
const snapshot = await box.snapshot("before-upgrade") // waits until the snapshot is complete
console.log(snapshot)
// => { id: "3f2a9b1c-7d4e-4a5f-8b6c-1e2d3f4a5b6c", name: "before-upgrade", status: "complete", operationId: "…",
//      createdAt: "2026-11-05T10:04:12.000Z", completedAt: "2026-11-05T10:04:40.000Z" }

snapshot waits up to 15 minutes by default. Pass { timeoutMs } to change that.

In the Dashboard, open the Box and use Create snapshot on its Snapshots tab.

List snapshots

const snapshots = await box.listSnapshots() // newest first
// => [{ id: "3f2a9b1c-7d4e-4a5f-8b6c-1e2d3f4a5b6c", name: "before-upgrade", status: "complete", … }]

A snapshot's status is pending until its operation finishes, then complete, ready to restore or create Boxes from, or failed, with the reason in the operation's error (status). Any member of the organization can list a Box's snapshots. To find snapshots across the organization that you may create Boxes from, use cubicle.listSnapshots() in the SDK or GET /v1/orgs/acme/snapshots; the CLI has no command for this list.

Restore a Box in place

Restoring replaces the Box's whole disk with one of its own complete snapshots, so everything written to the disk since then is lost. Take another snapshot first if you may need it. The Box must be fully stopped, as for taking a snapshot, and stays stopped afterwards: start it when the restore is done. It keeps its ID, name, private IP address and grants, and a restore cannot bring back revoked access (what a restore changes).

const snapshot = (await box.listSnapshots()).find(
  (candidate) => candidate.name === "before-upgrade" && candidate.status === "complete",
)
if (!snapshot) throw new Error("no complete before-upgrade snapshot")

await box.stop()
await box.restore(snapshot.id) // waits until the restore is done
await box.start() // waits until the Box is ready

In the Dashboard, use Restore next to the snapshot on the Box's Snapshots tab.

Check a restore

This round trip writes a file, snapshots the Box, changes the file, restores and reads it back:

await box.exec(["sh", "-c", "echo before > state.txt"])
await box.stop()
const snapshot = await box.snapshot("before-upgrade")

await box.start()
await box.exec(["sh", "-c", "echo after > state.txt"])
await box.stop()
await box.restore(snapshot.id)
await box.start()

const check = await box.exec(["cat", "state.txt"])
console.log(check.stdout) // => "before\n"

Create a Box from a snapshot

A new Box created from a snapshot starts with a copy of that disk, and the source Box can keep running. The snapshot must be complete, its source Box must still exist, and the new Box goes in the same organization. You need a grant on the source Box, or the owner or admin role. Do not pass a base image: the new Box uses the source Box's.

The new Box is placed on the Node that holds the snapshot. If that Node is unavailable or full, the request fails with 503 capacity. Its vCPUs, memory and disk default to the source Box's. You can set any allowed size, but diskGiB must be at least the source Box's disk: a disk can grow but not shrink (size rules).

In the CLI and curl examples, replace SNAPSHOT_ID with the snapshot's ID from List snapshots.

const snapshot = (await box.listSnapshots()).find(
  (candidate) => candidate.name === "before-upgrade" && candidate.status === "complete",
)
if (!snapshot) throw new Error("no complete before-upgrade snapshot")

// Waits until the new Box is ready, unless you pass start: false.
const clone = await cubicle.boxes.create({ name: "job-2", snapshotId: snapshot.id, diskGiB: 24 })
console.log(clone.name, clone.id) // => job-2 6a1f0c3e-2b4d-4e8f-9a7c-5d3e1f2b4a6c

In the Dashboard, choose the snapshot under Image when you create a Box.

The new Box gets its own ID, name, private IP address, SSH host key and hostname box-job-2, and a grant for you only: grant other people access separately (what it gets).

The new Box's disk holds every home directory from the source Box. Other people's accounts are locked in it, but anyone with sudo in the new Box can read their files.

Keep or remove snapshots

You cannot delete a single snapshot: deleting a Box deletes all of its snapshots. To keep a snapshot's contents after you delete its Box, create a Box from it first (retention).

Troubleshooting

  • 409 conflict "stop the box before taking a snapshot" or "stop the box before restoring a snapshot": the Box's desired state is not stopped. Stop it and wait until it is stopped.
  • Operation failed with "box must be stopped": the request reached the Node before the Box finished stopping. Wait for state: stopped at the current generation, then try again.
  • 409 conflict "a snapshot or restore is in progress for this box": wait for the snapshot or restore operation to finish before you start, stop, hibernate or delete the Box.
  • 404 not_found "snapshot not found" on a restore: the snapshot belongs to another Box. You can only restore a Box from its own snapshots; create a Box from it instead.
  • 404 not_found "snapshot not found" on a create: the snapshot is in another organization, or its source Box was deleted.
  • 409 conflict "snapshot is not complete": wait until its status is complete. A failed snapshot cannot be used.
  • 400 invalid_request "disk must be at least N GiB (the snapshot's size)": raise diskGiB, or leave it out to use the source Box's size.
  • 400 invalid_request "choose a base image or a snapshot, not both": remove baseImage. The CLI says "choose --base or --snapshot, not both".
  • 503 capacity "the node holding this snapshot is unavailable" or "the node holding this snapshot has no capacity for this box": the new Box can only run on that Node. Try again later, or ask for a smaller size.
  • 409 conflict "box job-2 is still being created from a snapshot of this box" on a delete: wait until the new Box has left provisioning, then delete the source.
  • 403 forbidden "box grant or organization admin role required": ask an owner or admin for a grant on the Box.

On this page