Systems · 2026

myrun

Linux container runtime in Rust with zero crates: namespaces, cgroups, seccomp

Complete · source available

Overview

  • What: a Linux container runtime in Rust where every syscall, netlink message and the JSON and TOML parsers are hand-written against the kernel ABI.
  • Hardest problem: bringing a container up and tearing it down transactionally, with no leaked veth, cgroup or IP lease when any step fails.
  • How it is validated: 19 named fault-injection points, each fired by an integration suite that checks resources return to baseline, plus 205 tests.

myrun creates real containers with separate PID, mount, UTS, IPC, network and cgroup namespaces, a pivoted root filesystem, cgroup v2 limits, a bridged network with NAT and port publishing, capability dropping, no_new_privs and a seccomp filter. Three cooperating processes (CLI, shim and init) run each container, with a rollback stack for everything created on the way up.

Architecture

myrun isolation layers The CLI starts a shim, which starts init. Init pivots the root filesystem, enters pid, mount, network, uts, ipc and cgroup namespaces, joins a cgroup v2 hierarchy, drops capabilities, sets no_new_privs, installs a seccomp filter and finally runs the process. A rollback stack undoes each completed setup step in reverse if a later step fails. rollback stack undo in reverse CLI shim init rootfs · pivot_root namespaces pid mnt net uts ipc cgroup process seccomp no_new_privs capability drop cgroup v2
fig. 01 — myrun isolation stack · each setup step is pushed on a rollback stack and undone in reverse on failure

Highlights

  • Hand-written kernel ABI layer: clone3 with CLONE_INTO_CGROUP, pidfd, pivot_root(".", "."), capget/capset, BPF seccomp assembly, signalfd, and rtnetlink for veth, bridge, addresses and routes.
  • A ready/go/started handshake between parent and init over a socketpair removes the races between network setup and pivot_root; a sealed memfd copy of the binary mitigates CVE-2019-5736.
  • 14 commands (run, create, start, stop, kill, pause/unpause, rm, ls, inspect, stats, logs, wait, gc, info), TOML/JSON config files, named containers and id-prefix lookup.
  • Failure handling proven, not asserted: 19 named fault-injection points (MYRUN_FAULT=after_veth_create), and an integration suite that fires every point and checks veth, cgroup and IP-lease counts return to baseline.
  • 155 unit + 9 CLI tests that run without privileges, plus 41 privileged tests; Rust 1.70+, and no Cargo.lock to audit.
  • Documented limits (root only, no exec, no OCI images, cgroup v2 only, iptables shelled out) and six docs including bugs.md, which records every real bug found and its fix.

What I learned

Fault injection turned “cleanup works” from a belief into a measured property: I could fire each of the 19 points and count resources. The ready/go/started handshake came from chasing races between network setup and pivot_root. Keeping a bugs.md of every real bug made the design decisions easier to defend.

Limits

Root only, no exec, no OCI images, cgroup v2 only, and iptables is shelled out.