A checkpoint saves a running computer's state so it can be brought back later, or brought back many times. On Linux, CRIU can do this for a tree of processes without a virtual machine. It is the piece that would let a shared runtime restore a prepared desktop in well under the time a VM takes to boot. It is also where our Linux work is stuck. This note explains the tool, then the three failures, then why macOS has no equivalent.
CRIU in plain words
CRIU (Checkpoint/Restore In Userspace) freezes a process tree, writes everything it can see about it to image files, and later rebuilds the tree from those files.
| Term | Meaning |
|---|---|
| dump | Freeze the tree and write images: memory pages, open files, sockets, namespaces, locks, signals, timers |
| restore | Create new processes with the same pids, map the same memory, reopen the same files, then resume |
| images | The directory of files a dump writes; a restore reads them. Size grows with dirty memory |
--file-locks | Record and re-apply file locks. Off by default; a dump of a process holding a lock fails without it |
--root | The filesystem root the restored tree should see, for a tree that was checkpointed inside a chroot or pivot_root |
| pre-resume hook | An --action-script called when every process is rebuilt but still stopped. The moment to audit what the restored tree actually holds |
| external namespaces | A namespace CRIU is told not to dump or restore (for example a network namespace owned by something else); the image records only a key |
A restore that "finished successfully" means CRIU rebuilt the tree and resumed it. It does not mean the tree has the same locks, the same network, or the same view of the filesystem. That gap is where the failures below live.
Failure 1: a POSIX lock on a mapped file is lost (unresolved)
A tiny child process takes a POSIX read lock on a plain file A and on a file B that it also maps into memory through a second descriptor, the way SQLite maps its -shm file. Dump with --file-locks; restore with --file-locks and a pre-resume hook that lists every lock the restored tasks hold while still stopped.
With stock CRIU 4.3 the dump records two locks and the restore log reports applying two locks. The pre-resume census shows the lock on A present and the lock on B absent (author-claimed from an authoring sandbox; no result file on the branch; see EXP-009).
Why it matters: a desktop runtime full of SQLite databases (browser profiles, GNOME settings, LibreOffice registries) would restore with databases that believe they are unlocked. That is a silent fidelity failure, the kind that corrupts a reward without a crash. The full GNOME candidate run saw exactly this shape: after restore only the meta.db lock was present and the meta.db-shm lock was missing, and the gate correctly failed (EXP-008).
Failure 2: runc restores a container with no network
The OCI reference arm uses runc 1.5.2 driving CRIU 4.3 on the same desktop image, as a control: if the custom runtime fails and runc also fails, the method is suspect; if runc passes, the fault is ours.
The first runc restore reported success, with correct pid identity and cgroup limits, but the loopback interface inside the restored container was down with no address, and the restore log said no network image existed (author-claimed, no artifact; EXP-010). A layered probe put the cause at runc itself: its checkpoint code always asks CRIU to treat the network namespace as empty, whether or not the flag is given, so CRIU skips the network dump and restore. A minimal container with a network namespace held open by a separate holder process, handed to runc as an external namespace, restored with the loopback up and the namespace byte-identical before and after (author-claimed, minimal sleep container only). The desktop run with that fix is the next step, not a done one.
The consequence for the control: the runc restore that "passed" did so with no file lock held and with a broken network. It is not a complete passing cycle either.
Failure 3: a GNOME tree and its mount namespace
The custom rootfs slot mounts the same OSWorld Ubuntu image read-only, puts a private overlay on top in its own pid and mount namespaces, pivots root, and starts the image's own D-Bus, Xorg (dummy driver), GNOME and guest server. Three successive dump refusals were hit and fixed in source: a root mount that was a device file, a root that was not the mount namespace root, and a session leader outside the pid namespace. The first dump that succeeded was followed by a restore that crashed immediately without a --root argument; a --root restore contract was then added, and the next full cycle reached the lock failure above (all author-claimed, no logs on the branch; EXP-008). Numbers from those runs (dump seconds, image size) are not published as measurements because no log exists.
Earlier, in a separate sandbox probe, a smaller tree (Xvfb, a window manager, D-Bus, the guest server and Chromium) was checkpointed and restored with CRIU 4.2, with the restored clones sharing random-number state, cookies and hostname until a reseed step was added; the restore was faster than a cold start but missed its pre-registered 2x bar once hygiene was included (EXP-005, see that record for the exact ratios and their labels).
The macOS equivalent: there is none
macOS has no process checkpoint. Forking a Cocoa application without exec is unsupported (vendor claim, fork(2) documentation), and there is no page-merging mechanism like KSM (inference). So on macOS there is no prewarm-and-fork and no restore of a running application. What exists:
- APFS clone swap of the work tree. The reset renames a pre-cloned copy of the attempt's work tree into place, verifies it against a manifest, clones the next spare in the background, and advances the generation. A clone of a 20,000-file, 78 MB tree took 2.27 s versus 4.72 s for a plain copy, and deletion 0.8 s (measured on one M2 8 GB laptop on macOS 27.0, not on a Mac Studio). Cost grows with file count, not bytes. No real Chrome or LibreOffice profile has been timed.
- Generation counter. Same protocol as Linux: every mutating call carries a generation, a stale one is refused with 409.
- Two traps found. Cloning out of a snapshot silently made a full copy (measured on the laptop); reverting a snapshot needs root and an Apple entitlement (vendor claim).
- Not reset. Keychain, privacy grants, Launch Services registrations and preferences outside the manifest.
So the macOS arm restores files and relaunches applications; it never restores a running application. Its "restore" is a reset plus a relaunch, and every page that reports a reset time for macOS says so.
What a reader should take away
Checkpoint and restore is the mechanism that makes a shared runtime worth having on Linux, and the mechanism is not yet faithful for the processes a desktop actually runs. Until the lock gate passes, every Linux number about the custom runtime is about a runtime that restores incorrectly. We publish the failure rather than the number.