The Drums fork of CRIU: a memory template server and restore-fidelity patches that let drumsd fork and reset Linux desktops in milliseconds.
Website · Docs · Benchmarks · Console · Upstream CRIU
This file has two parts. The first describes the fork. The second, from "CRIU -- A project to implement checkpoint/restore functionality for Linux" on, is upstream's README, kept as it is.
Drums runs computer-use agents in isolated Linux desktops that all come from one running operating system. drumsd, the Drums host daemon, keeps one checkpoint per desktop image and restores it once per environment, forks a running environment into N copies of the same state, and resets a used environment back to its checkpoint. Every one of those operations is a CRIU restore, and the time each takes is the floor under the cost of a rollout.
This repository is upstream CRIU with the patches that make that work: a template server that holds one checkpoint's memory and forks a restore from it instead of reading the images again, fixes for the state a full desktop carries (file locks, nested namespaces, protection keys, interrupted syscalls, resource limits), and a way to give every restored copy its own range of host uids. It is not a general-purpose CRIU release. For CRIU itself, its documentation and its bug tracker, go to criu.org and checkpoint-restore/criu. Report problems with the Drums patches here.
| Upstream base | master at 2b7ac603c, criu: Version 4.3, tag v4.3 |
| Fork branch | drums-main (the default branch); git log v4.3..drums-main is the whole difference |
| Drums patches | 128 commits, listed below |
| Licence | unchanged: GPL-2.0 (COPYING), LGPL-2.1 for lib/, Expat (MIT) for images/ |
Every change is a commit on top of v4.3. In groups, with why each exists:
| Series | Commits | What it does | Why Drums needs it |
|---|---|---|---|
Memory template server (criu restore --template-server SOCK, criu/pageshare.c) |
11 | One server process reads a checkpoint once, lays out every task's private anonymous memory, and forks a child per restore request; the restored tree starts with its pages shared copy-on-write with the template. Later commits fill the template with parallel reads, keep VMA boundaries and soft-dirty bits as a normal restore would, reserve premap slots, take the client's oom_score_adj, and serve incremental images through their parent chain. |
Siblings restored from one checkpoint share their private anonymous pages copy-on-write until they write them, instead of each reading a private copy from the images. This is what makes creating, forking and resetting many environments from one checkpoint fast and memory-cheap. |
Incremental dumps of a restored tree (--prev-images-uptime, --parent-template-only) |
1 | A tree that was itself restored from a base image can be dumped incrementally against that base, with tasks started after the restore dumped in full. | Capturing a reached state writes only what changed since the checkpoint it was restored from. |
Nested pid, user, net, uts, ipc and mount namespaces (--nested-pidns) |
62 | Dump and restore trees whose tasks sit in their own pid and user namespaces below the root task, with the net, uts, ipc and mount namespaces those user namespaces own, zombies, threads, seccomp, sockets and mount propagation included. New crit x userns. |
Chrome's sandbox and other sandboxed programs create exactly these trees; upstream CRIU refuses to dump them. |
| Chrome sandbox fidelity | 11 | Groups as seen from the top user namespace, a gid the parent maps, the LSM label through thread-self, the dumpable flag across call_in_child_process(), an AppArmor profile in the test. Test: zdtm/static/userns_chrome01. |
Each commit removes one cause of a restored Chrome renderer crashing or exiting after the restore. |
--userns-remap [u:|g:]FROM:TO:COUNT |
5 | On restore, the root user namespace gets a different range of host uids and gids. Also an RPC field (userns_remap in images/rpc.proto). |
Every tree restored from one checkpoint would otherwise run in the host id range of the tree that was dumped; drumsd gives each restored copy its own range. |
| Lock fidelity | 1 | POSIX locks are set after the VMA file descriptors are closed. | POSIX locks restored before the VMA descriptors were closed did not match the dumped state. |
| Overlay index for fsnotify | 8 | One upper-layer index per overlay mount per dump, a watch resolved from its file handle before any walk, an optional persistent index cache (CRIU_OVL_INDEX_CACHE) for a read-only lower layer. |
A GNOME desktop on an overlay root has 316 inotify marks whose handle cannot be opened; one walk per mark was 130 s of a 130 s dump, with the tasks frozen. |
| RLIMIT_FSIZE and ghost files | 5 | RLIMIT_FSIZE is set after the restorer's last log write; no SIGXFSZ is left behind on dump; a shared reg_file_info path is restored before remap_open_lock is released. |
rtkit-daemon sets RLIMIT_FSIZE to 0 and took a SIGXFSZ from the restorer's own log writes (3.6 % of concurrent restores of a GNOME session failed); many tasks holding the same renamed-over dconf files raced on a shared path. |
--restart-interrupted-syscalls |
2 | poll(), nanosleep() and futex waits with a timeout that the dump interrupts start again instead of failing with EINTR. |
VS Code's file watcher treats EINTR from poll() as an error and was replaced after every dump. x86 only. |
| Restore and dump robustness | 10 | A restorer BUG() before the creds stage fails the restore instead of hanging; the pid namespace daemon stops before a killed init is waited for; SIGCHLD is blocked around the creds helper children; the parasite logs through a pipe; a dump unlocks only the network lock it took. Two new fault-injection points. |
Each came from a hang or a leaked lock seen while running many concurrent restores on one host. |
| Memory protection keys | 6 | pkey_mprotect, pkey_alloc, pkey_free in compel on x86; a VMA's protection key and an mm's allocated keys are dumped and restored. |
Chrome keeps memory under protection key 1 (PartitionAlloc thread isolation); a restore that dropped the key made Chrome crash with SIGILL. |
| Fork housekeeping | 6 | This README, SECURITY.md, the fork's CI, and three one-line fixes that CI found (a lint rule, a musl build of a zdtm test, the unit-test link). |
The complete list, oldest first, as of this README (git log --oneline --no-merges --reverse v4.3..drums-main is always current):
All commits
7b4628484lock fidelity patch: set POSIX locks after VMA fd close7465bed8ffsnotify: index each overlay mount once per dump instead of walking it per watch8173f44ddrestore: share private anonymous memory between restores of one checkpoint52b450a40pageshare: leave VMA boundaries and soft-dirty flags as a normal restore does16313610drestorer: unmap the gaps a memory template leaves in the premap area627686b7arestore: set RLIMIT_FSIZE after the restorer has written its last log line869335679zdtm: restore a task with several threads and RLIMIT_FSIZE 0956026786files-reg: restore the shared reg_file_info path before releasing remap_open_lockf25393594zdtm: many tasks hold their own open and mapping of two renamed-over files (ghost_open_race)d19dc86adpageshare: reserve premap slots in the template for VMAs a restore must premapea79ef5fffsnotify: optional persistent overlay index (CRIU_OVL_INDEX_CACHE) for a read-only lower layer5ee6286fcimages: pstree: add the ids of a task at every pid namespace level2f1874e91util: add parse_ns_ids() for the NSpid, NSpgid and NSsid lines87d6d9893unittest: check parse_ns_ids() against a nested pid namespacee8265cef5proc_parse: keep every pid namespace level of NSpid, NSpgid and NSsid893efb588seize: keep the pid namespace levels of every task and thread621b28ed4pstree: dump the ids of tasks at every pid namespace levelee76537c3crit: show the pid namespace levels in 'crit x ps'3c7ee37cfcriu: add the --nested-pidns optiond1b500742images: describe nested pid namespacesda4db5deddump: allow nested pid namespaces with --nested-pidnsfb96ec716clone-noasan: add clone3_with_pids_noasan()cd39c6d0cpstree: read the pid namespace levels of tasks on restorea16c23f51restore: create tasks with their pids at every pid namespace levelcf0bb8dc6zdtm: add pidns_nested02, pidns_nested03 and pidns_nested0440d7fc088compel: add the unshare syscallecc28baf2images: add the pid namespace for children of a taskae873ae13dump: keep the pid namespace for children of tasks9df20a8a3pstree: read and check the pid namespace for children on restoref3406071arestore: give tasks their pid namespace for children80daaa730zdtm: add pidns_unshare01 and pidns_unshare0221a718813images: describe the tree of user namespacesf3bc62d4fdump: collect the tree of user namespaces with --nested-pidns64472da45crit: add 'crit x userns'7ba56fac0test/others: add userns-tree6545160canamespaces: read the tree of user namespaces on restoreaa1dd82a1restore: create the nested user namespacese80b7d96crestore: create pid namespaces owned by a nested user namespace5298d1029restore: let tasks enter their nested user namespace234106ab2restore: create the threads of tasks in nested user namespacese7935fd38net: restore net namespaces owned by a nested user namespacecd590b95bdump: write nested net namespaces if the root task has none of its owndad7835a3dump: allow nested user namespaces with --nested-pidns2ecb44fc8zdtm: add userns_nested01, userns_nested02 and userns_netns01757dfa4b2test/others: userns-tree: restore the tree with nested user namespaces8571520b0namespaces: restore nested user namespaces that have no maps325686a9fdump: keep the user namespace and the credentials of zombiesbaf5b5998restore: give zombies their credentials and user namespaceb4f8a728azdtm: add userns_zombie01562ee6cb8zdtm: add userns_seccomp010b64561absockets: restore sockets of nested net namespaces in the one of criu56db0d764zdtm: add userns_sk016d25c9049image: share the ids of page images with the namespace dumpers4c66bc93fipc_ns: keep the shm pages of nested ipc namespaces apart02249462erestore: create nested uts and ipc namespaces in their ownerb8d455d5ddump: allow nested uts and ipc namespaces with --nested-pidns2dde9b4f2zdtm: add userns_utsipc016ccc845e9util: add cr_system_userns_fd()1cf07b966dump: keep the pid namespace of proc mounts and the owner of tmpfs2d9516ebbmount-v2: restore mount namespaces owned by a nested user namespacefac36dbf2zdtm: add userns_mntns01e1671e1c6mount: export mnt_build_tree() and mnt_fsmount()e8200ab89images: describe copied mount namespacesa3f99eb92mount: restore nested mount namespaces of a root task in the one of criu13fcb3d2czdtm: add userns_container013a82d6fb3mount: restore copied mount namespaces with mount-v2 too085bfc8cezdtm: check the locks of mounts in copied mount namespacesba050e763Documentation: describe the nested namespaces restored with --nested-pidns0502b29f4mount: fix the propagation of mounts of copied mount namespaces8fc1cd258zdtm: userns_mntns01: add a peer group and a shared slave78df183b2zdtm: userns_sk01, userns_mntns01: cover more of the restore3ef92e13fzdtm: add userns_chrome01, the namespaces of the Chrome sandbox2233ed189namespaces: rewind the images dir before looking for userns images983a96f6etest/others: add pageshare-userns, a template restore of nested user namespaces9fd0dcc16namespaces: create a nested user namespace with a gid its parent maps040144ddfzdtm: userns_chrome01: map only uid and gid 1000, as Chrome doesc798c41bddump, restore: keep the groups of tasks of nested user namespaces as seen from the top956754a2dzdtm: userns_chrome01: give the tasks groups their user namespaces do not map5979ff1b9restorer: set the lsm label through thread-selfe3183a780zdtm: userns_chrome01: run R1 and R2 in an AppArmor profile, as Chrome is7cd971e94compel, dump: add --restart-interrupted-syscalls72dd49a54zdtm: add restartblock01, poll() and a futex wait with a timeout across C/Rcede7a11afault: FI_RESTORE_CREDS_BUG makes the restorer fail restore_creds() and BUG()a245251b6restore: fail at once when a traced thread faults before the creds stage finishes08d0f8154test: fault 9 (a restorer BUG in a thread before the creds stage) must fail the restore, not hangade3f3da8zdtm: the nested-namespace tree checks compare R, S, D and I as one stateee9118f93pageshare: the forked restore takes the client's oom_score_adj118c95813test/others: add pageshare-oom, the oom_score_adj of a template restore8cd7f8437test/others/overlayfs: add run-ovl-index, overlay watches resolved without the full walk2b5ee7a56fsnotify: put the overlay mountpoint itself in the upper indexb2ea456e7fsnotify: keep one upper index per overlay mount35a5957dbfsnotify: resolve an overlay watch from its file handle before any walkbc393bbadtest/others/overlayfs: run-ovl-index: cover the lookup by file handle02afd0209fsnotify: write the overlay index cache file at most once per dumpfd87d43acfault injection 10: a creds helper child of criu exits before criu waits for it7a2f7f138dump: block SIGCHLD from fork to waitpid of the creds helper children514b8e2eczdtm: dump a task with RLIMIT_FSIZE 0/0 and check that no SIGXFSZ is lefte10195158compel: give the parasite a pipe for its log instead of the log file5018e7d1ffault injection 11: the restorer can't enter the pid namespace for children of its task208b8a517restore: stop the pid namespace daemon before waiting for a killed init358380f66restore: open the pid namespace for children before the task enters its user namespacefbbd7c20anet: a dump unlocks only the network lock it took549eedbc4dump: read the groups of a nested task from the user namespace of the root task, by its real pid9ab683f62zdtm: run userns_chrome01 in the uns flavor too (the root task in its own user namespace)7cb53173futil: keep the dumpable flag of the caller of call_in_child_process()a17b78241zdtm: userns_chrome01: a /proc dir of the zygote kept open, and the oom_score_adj of a rendererdfb7ae3faimages: the protection key of a VMA and the keys an mm has allocatedad88c44d2compel: add pkey_mprotect, pkey_alloc and pkey_free on x86f100f93c0dump: keep memory protection keys52ac59456restore: give back memory protection keysb8859f65ezdtm: add pkey01, memory protection keys across C/Rb4fbfddf1zdtm: run pkey01 in the uns flavor too (the root task in its own user namespace, as a hardened slot)c35422e0frestore: --userns-remap FROM:TO:COUNT gives the root user namespace other host ids4849fad3erpc: userns_remap, the --userns-remap ranges of a restore request70cb8672aDocumentation: --userns-remap06ec7210fzdtm: add userns_remap01, the root user namespace restored with other host ids (uns)bfdd3e062test/others: add userns-remap, a slot-like tree restored into another host id range9bc7812a9pageshare: fill the template with parallel preadsfaac1aad7log: per-VMA and per-pagemap lines at debug leveld4a05d1e1pageshare: madvise a template region only when its mark changes, and unmap a forked subtree's regions in the parent344fd0a2apageshare: fill the template from an incremental image's parent chain (planned preads at the parents' offsets)6600614badump, restore: --prev-images-uptime for a base the tree was restored from, --parent-template-only, and template restores of incremental images18f1aca3bREADME: describe the Drums fork; drop upstream scheduled workflows that cannot run here51113ff3ecompel: report the parasite log pipe error with strerror(), not %mb29bd6dc2zdtm: userns_nested01: include limits.h for PATH_MAXe79e45147unittest: add a stub for add_userns_remap()1a6491e4dci: a fork CI that can pass here; upstream's full matrix on demand- this commit: README, SECURITY.md, logo
Changes that are useful outside Drums are meant to go upstream. Nothing here is an upstream feature until it lands in checkpoint-restore/criu.
drumsd does not link CRIU. It builds this tree into one binary and runs it as
a separate process, the same way the stock criu command is used:
- Install. drumsd's installer clones this repository at the commit pinned
in its
install/lock.json, runsmake -j criu, installs the binary as/opt/drums/criu/bin/criu, runscriu check, and records the source commit and the binary's sha256. drumsd's configuration names that binary ("criu": "/opt/drums/criu/bin/criu"indrumsd.json). - One template server per checkpoint. For every checkpoint drumsd keeps a
warm
criu restore --template-server SOCK -D IMAGES -o LOG -v4 --pidfile PIDFILEin its own cgroup, protected from the OOM killer. It survives a drumsd restart and is adopted again from its pidfile. - One restore per create, fork or reset. A short-lived helper joins the environment's cgroup, unshares a private mount namespace, mounts the environment's overlay, and sends the restore command line with its stdio to the template server over the socket. The template server forks and that child runs the restore. The command line is the ordinary one:
criu restore -D IMAGES --root CROOT --restore-detached --file-locks --action-script HOOK
--shell-job --tcp-close --manage-cgroups=ignore --nested-pidns -v4
--userns-remap FROM:TO:65536 --pidfile PIDFILE -o LOG
--action-script points at drumsd's own hook binary, which runs CRIU's
action hooks (the lock census before resume, the cgroup move) without a
Python process.
4. Captures. A reached state is dumped with criu dump --track-mem --prev-images-dir BASE --prev-images-uptime USEC ..., so the new images hold
only what changed since the checkpoint, and a template server can serve them
through the parent chain.
5. Gates. After every restore drumsd checks the result (locks applied,
cgroup, mount table, netns, state hash, processes alive after resume) and
quarantines the environment if a check fails. Those checks live in drumsd,
not here.
The same steps as upstream. On Ubuntu 24.04 (what drumsd's installer runs on):
sudo apt-get install -y build-essential pkg-config git libprotobuf-dev libprotobuf-c-dev \
protobuf-c-compiler protobuf-compiler python3-protobuf libnl-3-dev libnet1-dev libcap-dev \
libbsd-dev libgnutls28-dev libnftables-dev libaio-dev libdrm-dev uuid-dev libnl-route-3-devgit clone https://github.com/Drums-Labs/drums-criu.git
cd drums-criu
make -j"$(nproc)" criu
./criu/criu --version
sudo ./criu/criu checkmake without a target also builds crit, libcriu and the compel
plugins; see upstream's INSTALL.md for other distributions and
for make install. CRIU builds are not byte-reproducible: two builds of the
same commit have different hashes, which is why drumsd pins and checks the
source commit, not a binary hash.
make -j"$(nproc)" -C test/zdtm
sudo ./test/zdtm.py run -t zdtm/static/userns_nested01 -t zdtm/static/userns_remap01 -t zdtm/static/pkey01The zdtm tests the Drums patches add are pidns_nested02..04,
pidns_unshare01..02, userns_nested01..02, userns_netns01,
userns_zombie01, userns_seccomp01, userns_sk01, userns_utsipc01,
userns_mntns01, userns_container01, userns_chrome01, userns_remap01,
pkey01, restartblock01, rlimits02 and ghost_open_race. Larger
scenarios are scripts under test/others/: pageshare-userns (a template
restore of nested user namespaces), pageshare-oom, userns-tree,
userns-remap, overlayfs/run-ovl-index and netlock-dump-fail.
sudo ./test/zdtm.py run -a runs upstream's whole suite.
CI on this fork (ci.yml) builds with gcc and
clang, runs make unittest, and runs the Drums zdtm tests above on a
GitHub-hosted runner; lint.yml is upstream's
linter (make lint, make indent). Upstream's full test matrix is kept as
upstream-ci.yml and runs on demand.
Upstream develops on criu-dev and releases on master. This fork tracks
master releases:
git remote add upstream https://github.com/checkpoint-restore/criu.git
git fetch upstream --tags
git merge v4.4After a merge: build, make unittest, run the Drums zdtm tests, and update
the base commit at the top of this file and in the table above.
To report a problem in the Drums patches, use GitHub's private vulnerability reporting on this repository (see SECURITY.md). Problems in CRIU itself go to upstream's process. Note that CRIU runs as root; the desktop processes it restores for Drums run as an unprivileged user inside their own namespaces, and nothing in this fork changes that.
GPL-2.0, as upstream: COPYING. Files under lib/ are LGPL-2.1
and files under images/ are Expat (MIT), see images/LICENSE. Drums
distributes this source so that anyone who runs a drumsd host can build the
exact CRIU it uses.
Beta. The template server, --nested-pidns, --userns-remap and the
fidelity fixes are what drumsd runs in production on Ubuntu 24.04 x86_64
hosts. Other architectures build but are not tested by this fork's CI.
Upstream's --lazy-pages and --exec-cmd cannot be combined with
--template-server.
CRIU (stands for Checkpoint and Restore in Userspace) is a utility to checkpoint/restore Linux tasks.
Using this tool, you can freeze a running application (or part of it) and checkpoint it to a hard drive as a collection of files. You can then use the files to restore and run the application from the point it was frozen at. The distinctive feature of the CRIU project is that it is mainly implemented in user space. There are some more projects doing C/R for Linux, and so far CRIU appears to be the most feature-rich and up-to-date with the kernel.
CRIU project is (almost) the never-ending story, because we have to always keep up with the Linux kernel supporting checkpoint and restore for all the features it provides. Thus we're looking for contributors of all kinds -- feedback, bug reports, testing, coding, writing, etc. Please refer to CONTRIBUTING.md if you would like to get involved.
The project started as the way to do live migration for OpenVZ Linux containers, but later grew to more sophisticated and flexible tool. It is currently used by (integrated into) OpenVZ, LXC/LXD, Docker, and other software, project gets tremendous help from the community, and its packages are included into many Linux distributions.
The project home is at http://criu.org. This wiki contains all the knowledge base for CRIU we have. Pages worth starting with are:
- Installation instructions
- A simple example of usage
- Examples of more advanced usage
- Troubleshooting can be hard, some help can be found here, here and here
As main usage for CRIU is live migration, there's a library for it called P.Haul. Also the project exposes two cool core features as standalone libraries. These are libcompel for parasite code injection and libsoccr for TCP connections checkpoint-restore.
True live migration using CRIU is possible, but doing all the steps by hands might be complicated. The phaul sub-project provides a Go library that encapsulates most of the complexity. This library and the Go bindings for CRIU are stored in the go-criu repository.
In order to get state of the running process CRIU needs to make this process execute some code, that would fetch the required information. To make this happen without killing the application itself, CRIU uses the parasite code injection technique, which is also available as a standalone library called libcompel.
One of the CRIU features is the ability to save and restore state of a TCP socket without breaking the connection. This functionality is considered to be useful by itself, and we have it available as the libsoccr library.
The project is licensed under GPLv2 (though files sitting in the lib/ directory are LGPLv2.1).
All files in the images/ directory are licensed under the Expat license (so-called MIT). See the images/LICENSE file.
