all posts

Snapshot vs restore vs fork vs clone, explained

Ajay Kumar··8 min read

Snapshot, restore, fork, clone. Four words that get used as if they were synonyms, and they are not. They describe different operations on a running microVM's state, and if you conflate them you'll reach for the wrong one — pay a cold boot when you wanted a fork, or lose a warmed-up cache when you thought you were branching from it. This is a terminology post. I built PandaStack on Firecracker, so the examples are Firecracker-shaped, but the distinctions hold for any VMM that does snapshot-restore.

Here's the one-line mental model before we go deep: a snapshot is a photograph you can print many copies of; a restore is printing one copy and bringing it to life; a fork is photocopying a living thing so both copies keep walking, each paying only for the pages it changes.

Snapshot: saving a running VM to disk

A snapshot captures the full state of a running microVM into persistent artifacts you can keep around. For Firecracker that's three pieces: the guest memory image (`vm.mem`), the device and vCPU state (`vm.state`), and the rootfs disk. Together they are a complete freeze-frame — every byte of RAM, every register, every open file descriptor the guest kernel was holding.

The key thing about a snapshot is that it's a save, not a move. The original VM keeps running (or is paused for the instant of capture and then resumed). You're not tearing anything down — you're writing a copy of "what this machine looks like right now" to storage so you can recreate it later, as many times as you want. A snapshot on its own does nothing; it's inert artifacts sitting in a bucket until something restores it.

A snapshot is state at rest. It doesn't run, doesn't cost CPU, and doesn't hold a network slot. It's just files — memory image, device state, disk — waiting to be brought back to life by a restore.

Restore: booting a new VM from a snapshot

Restore is the inverse of snapshot. It takes those saved artifacts and stands up a new running VM from them — it loads the memory image, hands the device/vCPU state to a fresh Firecracker process, and resumes execution exactly where the snapshot was taken. The guest never knows it was frozen; it wakes up mid-stride.

This is the trick behind sub-second "boot." A normal cold boot runs the kernel's power-on sequence and then all of userland init — on PandaStack that's about 3 seconds. A restore skips all of it. You don't boot a kernel; you load a memory image of a kernel that already booted. The restore step itself is around 49ms, and a full create (allocate networking, reflink the disk, fork Firecracker, load the snapshot, resume, health-check) lands at p50 179ms, p99 ~203ms. Same isolation as a VM, roughly the latency of starting a process.

There are two flavours of restore worth naming, because they have different cost profiles:

  • Full-file restore — the memory image is a local file, mapped with mmap and MAP_PRIVATE. Pages are read from disk lazily as the guest touches them (copy-on-write on the mapping), so the restore returns before the whole image is resident.
  • Demand-streaming restore — the memory image lives in object storage, not on the host. A userfaultfd handler intercepts each page fault and fetches the missing 4 MiB chunk over the network on demand. This lets a host serve a snapshot it never fully downloaded — the price is per-fault latency instead of a big upfront copy.

Both are restores. The difference is only where the memory comes from and when you pay for moving it. Snapshot-then-restore is how you get a fast cold create from a template and how warm-start / wake-from-hibernate works: the snapshot is the template, the restore is the create.

Fork (or clone): branching from a live state

Fork is where people's intuition usually breaks, because it looks like restore but isn't. A fork creates a child VM that shares the parent's memory and disk copy-on-write. The child doesn't boot and it doesn't reload from a saved artifact — it starts from the exact live state the parent is in right now: same process table, same open sockets, same warmed page cache, same model already loaded into RAM. It then diverges from the parent only on writes.

"Fork" and "clone" get used roughly interchangeably here, and the Unix analogy is the right one: this is `fork()` for whole machines. Just as a forked process shares the parent's pages copy-on-write until one side writes, a forked VM shares the parent's guest memory via MAP_PRIVATE page faults and the parent's disk via reflink or dm-snapshot. Nothing is copied up front; a page is only duplicated the first time either side modifies it. If you've seen the zygote / pre-fork pattern (Android apps, or pre-forking web servers) it's the same idea — pay the expensive setup once in a parent, then branch cheap children off it.

Because the child inherits live memory rather than replaying a boot, a fork lands fast: a same-host fork is 400–750ms (local reflink plus memory restore), and a cross-host fork is 1.2–3.5s (the memory has to be pulled to the new host first, then restored). The child is a fully independent VM the instant it exists — killing the parent doesn't touch it, and its writes never leak back.

A fork captures memory and disk state as of fork time — nothing more. Anything the parent hasn't committed to that state yet (an unflushed write buffer, an in-flight network response, data still sitting in a device queue) is not in the child. If it wasn't in RAM or on disk at the moment you forked, the child never had it.

Side by side

  • Snapshot — what: writes a running VM's full state (memory + device state + disk) to persistent artifacts. Cost: a pause-and-capture; produces files you store. When: you want a reusable template or a save point to restore later.
  • Restore — what: boots a new VM from a snapshot by loading its memory image and resuming, skipping kernel boot + init. Cost: ~49ms restore step, p50 179ms full create. When: fast cold create from a template, warm start, wake from hibernate.
  • Fork / clone — what: branches a child VM off a live parent, sharing memory + disk copy-on-write, diverging only on writes. Cost: 400–750ms same-host, 1.2–3.5s cross-host. When: branch from a specific warmed/primed state — N branches from one prompt, best-of-N, per-request isolation from a post-setup baseline.

When to reach for which

Use snapshot + restore when the starting state is generic and reusable. You bake a template once (install the runtime, warm the caches, snapshot it), and then every create is a restore of that same snapshot. Nobody's asking for a branch off one specific running machine — they just want a fresh, fully-provisioned VM fast. That's the whole cold-start story: a snapshot is the mold, a restore is one casting.

Reach for fork when the state you want to branch from is specific and live — when the expensive part isn't booting, it's everything you did after booting. You've loaded a 4 GB model into memory, or primed a database with a fixture, or walked an agent through a long setup conversation, and now you want ten independent copies that all start from exactly that point. Restoring a snapshot would work only if you'd snapshotted that exact moment; forking branches off the running VM directly, and the ten children share the model's pages in memory copy-on-write instead of each loading their own 4 GB.

This is the shape behind tree-of-thought agents (fork N children from one primed prompt, let each explore a different branch, keep the best), best-of-N sampling, and per-request isolation from a post-setup baseline (fork a fresh child per request off a configured parent, kill it after, no state leaks between requests). It's also, more entertainingly, how you fork a VM mid-thought so it can argue with itself: branch the agent at a decision point, run both sides of the argument in parallel in sibling VMs, and let them fight it out — same memory up to the fork, divergent from there.

What it looks like in code

In the PandaStack Python SDK the operations map to methods with the names you'd now expect. Snapshot a configured sandbox to freeze its state; fork it to branch live children off it. Here's the primed-baseline-then-fork pattern — do the expensive setup once, then fork cheap isolated children off it:

from pandastack import Sandbox

# 1. Boot one parent and do the expensive, shared setup ONCE.
parent = Sandbox.create(template="code-interpreter", persistent=True)
parent.exec("python3 -c 'import prime; prime.load_fixtures()'", timeout_seconds=120)

# 2. Optional: snapshot the primed state as a durable save point.
#    (Inert artifacts you could restore later into a brand-new VM.)
parent.snapshot()

# 3. Fork N children off the LIVE parent. Each starts from the exact
#    primed state, shares the parent's memory + disk copy-on-write,
#    and diverges only on its own writes.
branches = [parent.fork() for _ in range(4)]
try:
    for i, child in enumerate(branches):
        # Every child sees the fixtures already loaded — no re-setup.
        r = child.exec(f"python3 explore.py --branch {i}", timeout_seconds=60)
        print(f"branch {i}:", r.stdout.strip())
finally:
    for child in branches:
        child.kill()
    parent.kill()

The distinction the code makes concrete: `snapshot()` produces a save you can restore into a new VM later, while `fork()` branches a live child off the machine as it is right now. Same primed state either way — but a restore is a new casting from the mold, and a fork is a photocopy of the living original that keeps walking on its own.

So the honest summary: snapshot and restore are a matched pair for saving state and recreating it fast from a generic template; fork and clone are for branching cheap divergent children off one specific live machine. Know which problem you have — reusable starting point, or branch-from-here — and the right verb picks itself.

Frequently asked questions

What's the difference between a snapshot and a fork of a microVM?

A snapshot writes a running VM's full state (memory image, device/vCPU state, and disk) to persistent artifacts you store and can restore later into a brand-new VM — the original keeps running. A fork branches a child VM directly off a live parent, sharing its memory and disk copy-on-write, so the child starts from the parent's exact current state and diverges only on writes. Snapshot is a save; fork is a live copy.

What is restore, and why is it so much faster than booting?

Restore boots a new VM from a snapshot by loading the saved memory image and resuming execution, instead of running the kernel's power-on sequence plus userland init. Because you're loading a memory image of a kernel that already booted, you skip the whole boot path — the restore step is around 49ms and a full create is p50 179ms, versus about 3 seconds for a genuine cold boot.

Are 'fork' and 'clone' the same thing for VMs?

They're used roughly interchangeably. Both mean creating a child VM that shares the parent's memory and disk copy-on-write and diverges only on writes — the direct analogy to Unix fork(). Some platforms name the method fork() and others clone(); the operation is the same branch-from-live-state idea.

When should I use fork instead of snapshot-and-restore?

Use fork when the state you want to branch from is specific and live and the expensive part is what you did after booting — a loaded model, primed fixtures, a long agent setup. Forking N children off that primed parent shares its memory copy-on-write, so they don't each redo the setup. Use snapshot + restore when the starting point is a generic, reusable template and you just want a fresh fully-provisioned VM fast.

Does a fork capture everything the parent VM had?

It captures the memory and disk state as of fork time, and nothing more. Anything not yet committed to that state — an unflushed write buffer, an in-flight network response, data still in a device queue — won't be in the child. If it wasn't in RAM or on disk at the moment you forked, the child never had it.

Run code in a microVM in one API call.

49ms p50 cold start. Fork, snapshot, and scale to zero.

Start free
Written by Ajay Kumar, Founder, PandaStack.