doos

User-level containers in C++20: overlayfs, user/mount/pid namespaces, slirp4netns networking, optional images (Alpine, Ubuntu, or your own rootfs), a size cap per container. No root at runtime.

doos list
doos get_or_create [--image I] [--size S] [--net M] [--ids X] NAME
doos attach        [--image I] [--size S] [--net M] [--ids X] NAME [-- CMD...]
doos delete [--yes] NAME
doos image list | add [--sha256 HEX] SOURCE [NAME] | delete [--yes] NAME

Version: see VERSION (currently 1.0.0). Release history: CHANGELOG.md.

User-level containers in C++20: overlayfs over the host's /usr, /etc, /opt, user/mount/pid namespaces, networking through slirp4netns. No root at runtime.

doos list
doos get_or_create [--net slirp|host|none] NAME
doos attach [--net slirp|host|none] NAME [-- CMD...]
doos delete [--yes] NAME

Build: cmake -S . -B build && cmake --build build. Test: tests/run.sh.

Requirements: Linux >= 5.11, slirp4netns (default network mode), uidmap (newuidmap/newgidmap, for more than one id inside; falls back to one id), curl, tar and sha256sum (images). On Ubuntu 24.04+ install the AppArmor profile once:

sudo cp apparmor/doos /etc/apparmor.d/doos && sudo apparmor_parser -r /etc/apparmor.d/doos

See SPEC.md (behavior, limits) and docs/RESEARCH.md (what works, measurements, known gaps).

Usage examples

doos attach dev                         # host mode: your system's /usr as base, shell as root inside
doos attach --image alpine box          # first use downloads and verifies Alpine, then opens a shell
doos attach --image ubuntu web -- apt-get update
doos attach --image ubuntu-24.04 old    # a specific Ubuntu release
doos attach --size 8g big               # raise the writable-layer cap (default 4g)
doos attach --net none sandbox -- sh    # no network at all
doos list
doos image list                         # what is installed, what the catalog offers
doos delete dev                         # dry run: shows what would go
doos delete --yes dev                   # really deletes
doos image delete --yes alpine          # refused while a container uses it

Your own image (a root filesystem tarball):

doos image add https://example.org/debian-rootfs.tar.xz --sha256 <hex> debian   # URL needs the hash
doos image add ./rootfs.tar.gz mybase                                           # local file
doos attach --image mybase c1

Host mode or image mode

Host mode (default) Image mode (--image)
Base system your /usr, /opt, copy of /etc the image, extracted once and shared
Needs a download no first use only
apt-get install works when the host is Debian/Ubuntu based works for the image's own package manager (apt, apk)
Sees host files /usr, /opt, readable /etc at creation nothing from the host
Best for quick throwaway shells with your tools reproducible, separate systems

Writes always stay in the container (copy-on-write); /root and /var persist, /tmp is memory-backed and lost on exit. A container is running while its first attach command is alive; more attach calls join it. Data persists until delete --yes.

Size cap

--size limits what a container writes (default 4g, 64m to 1t). The parent checks every 10 s: a warning at 80%, at 100% the container is frozen (nothing is deleted), and it thaws on its own below 95% after you free space with a second doos attach NAME -- .... Needs cgroup v2 delegation (standard on systemd desktops); without it the cap only warns.

Configuration

Variable Meaning
DOOS_HOME State directory (default ~/.local/share/doos). No , or : allowed.
XDG_DATA_HOME Used as $XDG_DATA_HOME/doos when DOOS_HOME is unset.

Troubleshooting

Message Cause and fix
write /proc/<pid>/uid_map ... blocked or mount ...: Permission denied AppArmor restricts user namespaces. Install apparmor/doos (command above).
slirp4netns failed to start Install slirp4netns, or use --net none / --net host.
subuid mapping unavailable Install uidmap and make sure /etc/subuid and /etc/subgid list your user, or use --ids single.
sha256 mismatch The download is not what the publisher lists; nothing was installed. Retry or check the source.
a URL image needs --sha256 Unverifiable input is rejected by design; pass the hash from the publisher.
size cap only warns, cannot freeze No delegated cgroup v2 (no systemd user manager). Cap is advisory.
doos: seeded ...: unreadable skipped: N Host entries the user cannot read were left out of the container. Informational.
state path must not contain ',' or ':' Set DOOS_HOME to a plain path.
container 'x' is running on delete Exit the attached command first.
container limit reached (64) Delete one first.

Safety summary

Host files are never written: system dirs are reached only through overlayfs. Host /home, /sys and host loopback are not reachable in the default mode. Images are verified against the publisher's checksum before they are extracted. --net host removes network isolation and is opt-in. Known gaps (no cgroups, readable host /etc) are listed with researched solutions (G1 to G6, not implemented) in docs/RESEARCH.md.

Project docs

File Content
SPEC.md Behavior contract and limits
docs/ARCHITECTURE.md Code map and flows
docs/RESEARCH.md What works unprivileged, measurements, gaps
AGENTS.md Rules for AI agents, release workflow
CHANGELOG.md Release history
S
Description
User-level containers in C++20: overlayfs, user namespaces, slirp4netns/pasta networking, cgroup v2 limits, seccomp hardening. No root at runtime.
Readme MIT
228 KiB
Languages
C++ 72.4%
Shell 24.5%
Makefile 2.7%
CMake 0.4%