Skip to content
Raster
Esc
navigateopen⌘Jpreview
On this page

Snapshots

Capture a disk, restore onto it, fork a machine from it, or name it as a template.

Three things stand on one mechanism — capturing a machine’s disk — and differ in what they are for.

What it is Reach for it when
Snapshot a captured disk, belonging to one machine you want to be able to go back
Fork a new machine from a copy of a disk you want to branch and run both
Template a named, organization-level starting state you want every new machine to start here

What a capture holds

Files, installed software, and browser profile data — cookies, logins, extensions.

It holds no RAM and no live process state. Restoring boots the disk; it does not resume a session. A process that was running is not running afterwards. That is a property of the substrate rather than a setting, so there is nothing to turn on.

Design for it: write state to disk, not to a long-lived process. Secrets are deliberately not carried by a capture.

Snapshot

const snap = await machine.snapshot({ name: "after-setup" });
snap.state; // "ready" — this waited for the capture

const pending = await machine.snapshot({ name: "later", wait: false });
snap = machine.snapshot(name="after-setup")
snap["state"]   # "ready" — this waited for the capture

pending = machine.snapshot(name="later", wait=False)

The machine has to be running — a disk is captured from a live instance. The capture is asynchronous on the server and the SDKs wait for it by default, so what you get back is immediately restorable. wait: false gives you the pending record to poll yourself.

for await (const s of machine.snapshots()) console.log(s.id, s.state, s.origin);

await client.snapshots.waitUntilReady(pending.id);
await client.snapshots.get(snap.id);
await client.snapshots.delete(snap.id);
for s in machine.snapshots():
    print(s["id"], s["state"], s["origin"])

client.snapshots.wait_until_ready(pending["id"])
client.snapshots.get(snap["id"])
client.snapshots.delete(snap["id"])

origin is manual (you asked), stop (taken automatically when the machine stopped) or template (the capture behind a template).

Only the newest capture of a machine can be deleted. Captures form a chain in the order they were taken, so removing an older one raises a conflict naming the snapshot in the way. Delete forward; there is no flag to override it.

Restore

await machine.restore(snap.id);
machine.restore(snap["id"])

The current disk is captured first, under the name “Before restore”, so the state being replaced stays recoverable. The machine then restarts: a running one comes back running, a stopped one boots into the restored disk next time it starts.

Anything open on the machine is disconnected, and nobody may be holding the input lease — the SDKs release theirs first.

Fork

const branch = await machine.fork({ name: "branch-a" });
await branch.waitForDesktop();
branch = machine.fork(name="branch-a")
branch.wait_for_desktop()

Without a snapshot id the current disk is captured first, which needs the machine running. With one, the fork stands on bytes that already exist — which is how you fan out several machines from a single capture without paying for the capture each time:

const base = await machine.snapshot({ name: "configured" });
const workers = await Promise.all(
  ["a", "b", "c"].map((n) => machine.fork({ snapshotId: base.id, name: `worker-${n}` })),
);
base = machine.snapshot(name="configured")
workers = [
    machine.fork(snapshot_id=base["id"], name=f"worker-{n}")
    for n in ("a", "b", "c")
]

A fork comes back in creating, exactly like a create, so waitForDesktop is what waits for a usable computer. Afterwards it owes the original nothing: deleting the parent leaves every fork running.

Templates

const tpl = await machine.saveAsTemplate({ name: "research-box" });
const fresh = await client.machines.create({ template: tpl.id, size: "standard" });

for await (const t of client.templates.list()) console.log(t.name, t.state);
await client.templates.delete(tpl.id);
tpl = machine.save_as_template(name="research-box")
fresh = client.machines.create(template=tpl["id"], size="standard")

for t in client.templates.list():
    print(t["name"], t["state"])
client.templates.delete(tpl["id"])

A template is a snapshot with a name on it and an organization behind it — how a team agrees on what its standard box is. saveAsTemplate needs the machine running and waits for the capture, so what comes back is a template machines.create will accept.

client.templates.create also takes a snapshotId, or neither — a named image and size with no captured disk behind it.

Deleting a template leaves machines created from it running and keeps their disks: a machine copies the template’s bytes when it is created and owes it nothing afterwards.

Was this page helpful?