How to fork a computer
Every programmer knows fork(). You call it, and your one process becomes two, both continuing from the same line of code with the same variables, and from that moment they go their separate ways. It's one of the oldest tricks in Unix. What fewer people know is that you can do the same thing to an entire computer. Not copy its disk. Fork it. Take a machine that is running right now, with a browser open and a program halfway through a loop, freeze it at one exact moment, and wake up two of them.
I've spent the last few months reading every doc and paper on this, and most of them are written for people who already run data centers. This is the version I wish I'd had at the start. Plain language, no hand-waving about what's actually being copied, and honest about where the easy version stops and the hard version begins.
First, what is a running computer made of?
When you save a file, you're saving disk. When you take a "snapshot" of a VM in most cloud consoles, you're also mostly saving disk. That's not a fork. If you boot a copy of the disk, you get a fresh machine that has to start from zero: kernel boots, services start, your app loads, caches fill. You've cloned the hard drive, not the moment.
The moment lives in four places. The disk, sure. But also the memory, which is every open program, every variable, every browser tab, the kernel's own bookkeeping. The CPU registers, which are the handful of numbers that say exactly which instruction each core was about to run and what it was holding in its hands when you froze it. And the device state: what the network card was in the middle of sending, which keys the keyboard controller thinks are held down, where the clock is. Fork all four and you have the moment. Fork fewer and you have a reboot.
The trick that makes it fast
Here's the obvious objection. A machine with 8GB of memory has 8GB of memory. If forking means copying all of it, a fork should take as long as copying 8GB, which is seconds at best. But real forks happen in single-digit milliseconds. How?
The answer is that you don't copy. You share, and you only copy the parts that change. This is called copy-on-write, and it's the same trick the original fork() uses. When you fork a process, the kernel doesn't duplicate its memory. It marks every page (memory is handed out in 4KB pages) as shared and read-only, and lets both processes read the same physical pages. The instant one of them tries to write to a page, the kernel catches it, quietly makes a private copy of just that page, and lets the write go through on the copy. The other process still sees the original. Ten forks of a 500MB process cost almost nothing until they start writing, and even then they only pay for the pages they touch.
Forking a whole machine does the same thing one level down. The hypervisor, which is the program that pretends to be hardware for the VM, saves the guest's memory to a file once. Then every child maps that file as its memory, read-only, shared. When a child writes to a page, the host gives it a private copy of that page. The parent's memory file never changes. A child that touches 50MB of memory in its first minute costs 50MB, not 8GB. And the fork itself is just setting up the mapping, which is why it's milliseconds.
Two honest caveats on that number, because the marketing pages leave them out. First, "the child appears in milliseconds" and "the child is doing useful work in milliseconds" are different claims. Setting up the mapping is fast. But the moment the child runs, it starts touching memory, and every page it touches for the first time has to be pulled in from the file. If that file is on a local SSD and warm in the host's cache, that's quick. If it's cold, or on a network disk, the child spends its first few hundred milliseconds stuttering through page faults before it feels alive. The {L('https://dl.acm.org/doi/10.1145/3492321.3524270','FaaSnap paper')} is a good read on exactly this gap. Resume latency is what gets quoted. Time-to-useful-work is what you actually feel.
Second, sharing memory only works when the children are on the same physical machine. Two VMs on the same host can literally read the same RAM pages. Two VMs on different hosts cannot, because RAM doesn't stretch across a network. What you can do across hosts is share the file: chop the snapshot into chunks, store them in a cache, and have each child pull only the chunks it needs, on demand. {L('https://www.buildbuddy.io/blog/fast-runners-at-scale/','BuildBuddy wrote up')} a system that does this for CI runners. It's the same copy-on-write idea, just with a network in the middle, and every host still has to end up holding its own copy of whatever pages its child touches. So: one snapshot, many children on one box is cheap. One snapshot, many children across a fleet is a real distributed systems problem.
The disk works the same way. Nobody copies the disk image. Each child gets a thin overlay file that starts empty, and any block the child writes goes into the overlay. Reads check the overlay first and fall back to the parent's disk. QEMU calls these qcow2 backing files. Docker's layered images are the same idea. The parent disk is frozen forever, and each child's overlay is its diff.
The four ways people actually do it
Firecracker is what I'd point anyone at first. Amazon built it to run Lambda, and it's a tiny hypervisor that boots a stripped-down VM in about a hundred milliseconds. Its snapshot support is the cleanest version of the fork I've described. You pause the VM, ask it to write out two files, one for memory and one for everything else (CPU, devices), and then you can start as many new Firecracker processes as you want from those two files. Each one maps the memory file privately, so writes are copy-on-write for free, courtesy of the Linux kernel. The API is three HTTP calls:
# freeze the parent and save it
PUT /vm {"state": "Paused"}
PUT /snapshot/create {"snapshot_type": "Full",
"snapshot_path": "vm.state",
"mem_file_path": "vm.mem"}
# in a brand-new firecracker process, wake a child from it
PUT /snapshot/load {"snapshot_path": "vm.state",
"mem_backend": {"backend_type": "File",
"backend_path": "vm.mem"},
"resume_vm": true}
Run that last call ten times and you have ten children, all mid-sentence, all sharing one memory file. This is what E2B and most of the newer sandbox companies are doing under the hood when they advertise sub-200ms starts. One constraint the docs are blunt about: a snapshot only restores on a host with the same Firecracker version, same kernel, same CPU features, same device layout. It's a saved brain, and it only fits back into an identical body. "Resume anywhere" really means "resume anywhere in a pool of matching machines," which is a fine thing to build, but not the same thing.
VMware Instant Clone is the enterprise version, and honestly it's the one that made the word "fork" stick for VMs. Their marketing used to literally call it VMFork. You keep a parent VM running, and the child is created from the parent's live memory and device state without ever going through a boot. The child shares the parent's physical RAM and disk via copy-on-write and is usable in a second or two. VDI shops (virtual desktops for office workers) use it to spin up a thousand identical Windows desktops every morning from one warmed-up parent, which is the same "a thousand agents from one prepared machine" story wearing a suit.
QEMU/KVM is the general-purpose Linux hypervisor, and it can do all of this, but with more assembly required. You save the live state of a running VM with savevm or virsh snapshot-create-as, which writes memory and device state into the disk image or a file. To fork rather than just restore, you point multiple new VMs at that saved state with their own qcow2 overlays for disk. QEMU won't share memory between children by default the way Firecracker does, so each restore reads the full memory back, and you lose the millisecond number unless you do extra work. It's the right tool if you need a full-fat VM with GPUs and weird devices. It's the wrong tool if you want a thousand forks.
CRIU, Checkpoint/Restore In Userspace, is for when you don't want a whole machine, just a process or a container. It freezes a running Linux process tree and dumps everything about it, memory, open files, sockets, even TCP connections, into a folder. Restore that folder and the process picks up where it stopped, on the same machine or a different one. Restore it three times and you've forked the container. It's heavier than the VM approach because it's a real save-then-load rather than a memory mapping trick, but it works without a hypervisor and it's what Docker and Podman use for checkpointing.
The children wake up confused
This is the part the tutorials skip, and it's the part that actually bites you. A child is a perfect copy of the parent at the frozen moment. Perfect is the problem. The parent had a network card with a MAC address, an IP address, a set of open TCP connections, a random number generator that was seeded at boot, a system clock, a hostname, maybe a TLS session or an SSH connection. Every child inherits all of it, bit for bit.
So you wake up ten children and you have ten machines with the same MAC address on the same network, which the network hates. Ten machines that think it's 2:14pm when it's actually 2:31. Ten machines with an open TCP connection to a server that only remembers one of them, so nine of them are talking into a dead socket and don't know it. And worst of all, ten machines whose random number generators are in the exact same state, so they'll generate the exact same "random" session tokens, the same nonces, the same temp file names. If any of them mints a cryptographic key right after waking, all ten mint the same key. Firecracker's own docs have a page specifically about this, and it's not a footnote.
So every real forking system has a "wake-up" step that runs inside the child immediately after it resumes. Give it a new MAC and IP. Reseed the random number generator. Sync the clock. Tell the guest its hostname changed. Expect every old connection to be dead and reconnect. VMware runs a customization script for this. Firecracker's docs tell you to handle it in the guest yourself. And that word, "yourself," is doing a lot of work. The hypervisor can fix the things it can see: network card, clock, the entropy pool. It cannot see inside your programs. It has no idea that your app holds an OAuth token in a variable, that Chrome has a logged-in session in memory, that Postgres has a connection open with a password in it, or that a background job just generated an encryption key. All of that is duplicated too, and nothing at the hypervisor level knows it exists, let alone how to rotate it. You write that part, per application, and you will get it wrong at least once in a way you only discover when two children try to talk to the same database.
There's a deeper version of this problem that I wrote about in the three layers of agent persistence. The MAC address is a small identity problem. The child also inherits every logged-in session, every API token in memory, every cookie in the browser. Now you have ten agents that all look like the same user to every service they touch. Forking the machine on one host is a solved problem. Deciding what the children are allowed to be is not.
The four levels, and where the solved part ends
Once you've read enough of this, you notice that "fork a computer" is being used to mean four different things of very different difficulty. It helps to name them.
Level one is a solved problem and has been for years. Level two is what this whole post has been about, and the tools exist. Level three is where it stops being a hypervisor feature and becomes a storage and scheduling problem: the state has to outlive its host, move over a network, and only land on a machine with a matching CPU, kernel, and device layout. Level four is level three plus time. The state isn't a file you restore once. It's a tree that keeps growing, with branches that sleep for hours, wake up on whatever machine has room, get forked again, and share as much as possible with their siblings along the way. Nobody has a complete, public, general version of that yet.
What a real fork would have to carry
Here's the reframe that made the gaps obvious to me. A snapshot today is two files: memory and machine state. But if you think about what a child actually needs to wake up safely, the list is longer, and everything after the first two items is where the unsolved work lives.
It needs the memory and CPU state, obviously. It needs the disk layers. Then it needs a compatibility manifest, a note saying which hosts this can even be restored on. It needs an identity contract: which of the inherited logins, tokens, and keys this branch is allowed to keep, and which get revoked or swapped on wake. It needs a network contract: what address it gets, what it may connect to, whether the dead connections are reconnected or refused. It needs a time and randomness policy, and here's a fun wrinkle, the right policy depends on what you're using the fork for. If you're running a reinforcement learning experiment and want ten branches to be exactly reproducible, you want the clock and the random seed to be identical across children. If you're running ten production agents, identical randomness is a security hole. Same mechanism, opposite requirement. And finally it needs some record of external side effects, because if the parent sent an email or charged a card before the fork, ten children continuing from that moment must not each do it again.
None of that is exotic. It's the kind of bookkeeping distributed systems people do all day. But none of it lives at the hypervisor layer, and none of the four tools above will do it for you. That gap, between "we can copy the bytes" and "the copy is safe to let loose," is the actual frontier.
Where this is going
All four tools above were built for other reasons. Firecracker for serverless functions, VMware for virtual desktops, QEMU for general virtualization, CRIU for migrating containers between servers. None of them were designed for the thing that's suddenly pulling on them, which is an AI agent that's spent twenty minutes getting a machine into a useful state and now wants to try three things from there at once.
That use case is different in one specific way: the fork isn't a rare event. Serverless forks once per warm start. Agents want to fork before every risky action, every time they're unsure, every time a task has two plausible next steps. That pushes the numbers down from "milliseconds is fine" to "single-digit milliseconds, hundreds of times an hour." The research is already there. TClone forks a live desktop with the GUI still running. DeltaBox gets checkpoint and rollback to about 14 and 5 milliseconds by only ever saving the delta and forking from a frozen template process. Both are copy-on-write again, applied more aggressively.
The mechanism is not new. Unix figured out that you fork by sharing and copying only on write, fifty years ago, for processes. Hypervisors borrowed it for machines. The thing that's new is who's asking, and how often. If you understand that one trick, sharing until someone writes, you understand every system on this page. The rest is plumbing and the very real headache of ten machines that all think they're the same machine.
If I had to compress all of this into one sentence, it's this: a running computer should be a portable state object, not a machine. We're not there yet. But every tool on this page is a step toward it, and the steps are getting shorter.