Files
doos/COMPARISON.md
T

17 KiB

doos compared with Docker, Podman and others

Docs: README · SPEC · ARCHITECTURE · RESEARCH · COMPARISON · AGENTS · CHANGELOG

This page puts doos next to the tools people usually reach for instead. It separates three kinds of claims, and every row says which one it is:

  • [M] measured on the development machine, described in "Measurements" below.
  • [S] sourced from the linked documentation or articles. Vendor blogs are marked as such and their numbers are not used.
  • [D] design: what the doos source and SPEC do today.
  • [K] general knowledge that this research did not re-verify.

Nothing here is a roadmap. Where doos lacks something, the gap is listed with the code that would have to change.

Versions compared: doos 1.1.1, Docker 29.1.3 (rootful daemon on this machine), Podman 5.7.0 (netavark, crun, overlay), bubblewrap 0.11.1, on Ubuntu 26.04, kernel 7.0.0-38, 12 cores, one machine.

1. At a glance

doos Docker (rootful) Docker rootless Podman rootless
Needs a daemon no [D] yes, root daemon [S: Docker security] yes, per-user daemon [K] no [S: rootlesscontaine.rs]
Runs without root yes [D] no [S] yes [S] yes [S]
Who can use it safely any user only trusted users: access to the daemon is root-equivalent [S: Docker security, iximiuz] any user any user
Image source tarball, https URL with sha256, small catalog (Alpine, Ubuntu, Debian) [D] OCI registries, Dockerfile builds [K] same as Docker OCI registries, Containerfile builds [K]
Build images no [D] yes (BuildKit) [K] yes yes (Buildah) [K]
Host dirs into the container (-v) no [D] yes [K] yes yes
Published ports none for slirp; automatic for pasta [D] -p [K] -p -p [S: podman-rootless(7)]
Rootless network backend slirp4netns or pasta [D] n/a RootlessKit based [S: Podman blog] pasta default since 5.0 [S: podman-rootless(7)]
Resource limits memory, pids, cpus, written data [D][M] memory, cpu, pids, more [K] needs cgroup v2 + systemd delegation [S: Docker rootless tips] needs cgroup v2 [S: podman-rootless(7)]
Default syscall filter denylist: 48 listed calls plus rules for clone, unshare, clone3 [D] allowlist profile [S: Docker seccomp] same built-in profile [K]
Default capabilities Docker's default set [D][M] that set [S] that set a smaller set [S: dev.to comparison]
Orchestration (Compose, Kubernetes) none [D] Compose, Swarm, Kubernetes via runtimes [K] same Compose, pods, kube play, systemd units [K]
Background containers, logs, stop none: a container lives while its first attach command lives [D] yes [K] yes yes
Binary size 552 KiB, 6 shared libraries, 3,445 lines of C++ [M] CLI 29.9 MiB, plus dockerd and containerd [M][K] same 41 MiB [M]
Platforms Linux only, tested on x86_64 only [D] Linux, macOS, Windows (via a VM) [K] Linux Linux; macOS and Windows via a VM [K]
Maturity version 1.1, one test machine very mature, huge ecosystem mature mature

2. Other tools, one line each

Tool What it is Versus doos
bubblewrap (bwrap) Low-level unprivileged sandbox builder, the engine behind Flatpak [S: bubblewrap README, Julia Evans] Smaller and faster to start [M], but you assemble every mount by hand and it has no image handling, no persistent state and no cgroup limits [K]; it maps a single id unless you add a helper [K]. doos is the "batteries included" layer on the same kernel features.
systemd-nspawn Boots a whole OS tree as a container [S: forum overview] Rootless use is limited to a single UID/GID inside [S]. Not installed here, so not measured.
LXC / Incus (LXD fork) System containers: a full distro with init and several services, close to a light VM [S: OneUptime] Better for long-lived pet systems and multi-service setups, with API, storage pools and profiles. Heavier to set up. lxc binaries exist here, Incus does not, neither was run.
Apptainer (Singularity) Rootless, daemonless, single-file SIF images, built for HPC (MPI, InfiniBand, GPUs) [S: Alliance Canada, NYU HPC] Different audience. It also has --fakeroot and signed immutable images [S], which doos lacks. Not installed here.
Distrobox / Toolbx Podman- or Docker-backed "your distro inside your terminal", home directory shared on purpose, not a sandbox [S: Arch wiki, comparison] Opposite goal: doos hides the host's /home by default.
gVisor A user-space kernel that answers most syscalls itself, cutting host-kernel exposure at an I/O cost [S: Edera, Northflank] Stronger isolation than doos, needs its own runtime. Vendor blogs; their numbers are not used here.
Kata Containers / Firecracker Each workload in its own KVM micro-VM with its own kernel [S: same sources, MASCOTS 2020 paper] The right tool for hostile or multi-tenant code. doos shares the host kernel, so a kernel bug is a doos escape.

3. Measurements

Method: trivial command (true) started 10 times in a row, average wall time per run, warm caches, the Alpine image already present for every tool, one machine, one run set. Scripts were ad-hoc shell loops and are not part of the repository tests, so treat the numbers as indicative, not as a benchmark.

Setup Time per run
bubblewrap, host /usr, all namespaces unshared, no network 9 ms
bubblewrap, host /usr, network shared 8 ms
doos host mode, --net none 14 ms
doos image Alpine, --net none 16 ms
doos host mode, slirp 54 ms
doos image Alpine, slirp 57 ms
doos image Alpine, pasta 65 ms
Podman, Alpine, --network none 214 ms
Podman, Alpine, default network 234 ms
Docker (rootful), Alpine, --network none 590 ms
Docker (rootful), Alpine, default bridge 456 ms

How to read this honestly:

  • doos is fast partly because it does less: no image store database, no registry code, no layer unpacking per run, no container metadata service. The extra time over bubblewrap is the id map helpers (newuidmap, newgidmap), the overlay mounts, a hardening filter and the network helper process.
  • Docker here is a rootful daemon, so its numbers include a client-to-daemon round trip. Rootless Docker was not measured.
  • The Docker row "none" is slower than "default" in this run; the spread between runs was not measured, so do not read a ranking into it.
  • Startup is one number among many. It matters for scripts that start hundreds of short-lived sandboxes. It does not matter for a service that starts once.

Disk, measured with du (which cannot read overlayfs' mode-000 work directories, so true usage is slightly higher):

Item Size
doos Alpine image, extracted once and shared 8.3 MiB
doos Ubuntu base image, extracted once and shared 98 MiB
one doos container on top of an image a few KiB of metadata plus its own writes
four containers (one Alpine, three Ubuntu) on shared images 20 KiB together
doos host-mode container after its first create (skeleton, /etc allowlist, dpkg state) 4 MiB, created in 0.5 s
Podman storage on this machine (many unrelated images) 26.3 GB images, 1.8 GB containers

The Podman figure is this machine's accumulated state, not a like-for-like comparison. What the comparison does show is the model: doos extracts an image once and every container adds only its own writes.

4. doos versus Docker

Where doos is better

  • No daemon and no root. With Docker's default install, anyone who can talk to the daemon controls the machine [S]. doos has no such trust boundary: it can only do what the invoking user can do, plus what the newuidmap helper allows.
  • Tiny and auditable: 552 KiB, 3,445 lines [M], one process tree, no background service, nothing listening.
  • Instant: 14 to 65 ms against 456 to 590 ms for a rootful Docker run [M].
  • Safe defaults a user does not have to remember: Docker's default capability set, a seccomp filter, an allowlist of host /etc, a memory and pid cap, a written-data cap [D][M].
  • A "host mode" that reuses the host's installed tools (compilers, apt state) for throwaway shells with no image download [D].
  • A size cap that freezes instead of filling the disk [D][M]. Docker's writable-layer size limit needs the overlay2 driver on XFS with project quotas [S: Fabian Lee].

Where Docker is better

  • Everything around images: registries, docker build, layer caching, multi-arch, signing and scanning ecosystems [K]. doos cannot pull an arbitrary nginx:1.27 from a registry.
  • Host directories and devices into the container (-v, --device, GPUs). doos has no bind-mount option [D].
  • Long-running services: background containers, restart policies, healthchecks, logs, exec, stop [K]. In doos a container exists only while its first attach command runs [D].
  • Networking: user-defined networks, DNS between containers, published ports on any interface [K].
  • Compose, Swarm and the Kubernetes ecosystem [K].
  • macOS and Windows support through a VM [K].
  • Strongest default syscall posture: an allowlist, which denies unknown new syscalls. The doos filter is a denylist, so a new kernel syscall is allowed until it is added [D][S].

5. doos versus Podman

Podman is the closest comparison, since both are daemonless and rootless.

Where doos is better

  • Smaller, faster to start [M] and one binary with no storage database. Podman's rootless setup brings a store under ~/.local/share/containers with its own metadata [K].
  • Opinionated limits and hardening out of the box, with a stated, measured effect (OOM kill, pids, cpus, freeze at a size cap) [M].
  • Two network backends with different, documented trade-offs, including a host-like mode that keeps host abstract unix sockets out of the container [M].
  • Image verification is explicit: sha256 plus signatures checked against fingerprints pinned in the source [D][M].
  • Frozen-at-the-cap behaviour with a never-frozen rescue shell [D][M].

Where Podman is better

  • OCI compatibility end to end: pull, push, build with Buildah, pods, kube play, Quadlet and systemd integration [K]. It can also run images made for Docker unchanged.
  • Docker-compatible CLI and socket, so existing tooling mostly works [K].
  • Bind mounts, volumes, secrets, exec, logs, stop, restart policies [K].
  • Years of hardening, a security team, and a large user base finding the sharp edges. doos has one test machine.
  • Podman documents rootless caveats: ports below 1024, /etc/subuid setup, cgroup v2 only, and a rootless port forwarder that does not preserve client source IPs [S: podman-rootless(7)]. doos shares the subuid and cgroup v2 requirements [D]. Binding ports below 1024 in doos was not tested.

What both share: the same kernel features (user, mount, pid, network namespaces, overlayfs, cgroup v2, seccomp), the same pasta/slirp4netns options, and the same weakness: they share the host kernel [S].

6. Security, side by side

  • Shared kernel. doos, Docker, Podman, bubblewrap, nspawn and LXC all run container processes on the host kernel. A kernel vulnerability reachable through allowed syscalls is a host vulnerability. gVisor reduces that exposure at a performance cost; Kata and Firecracker remove it by giving each workload its own kernel [S].
  • Rootless is the real difference from rootful Docker. A bug that gives root inside a rootless doos or Podman container yields the invoking user's rights on the host, not root [D][S].
  • doos filter style. A denylist built at runtime with an architecture check, x32 guard and clone3 handling [D]. Docker and Podman ship allowlist profiles [S]. An allowlist is the more conservative design.
  • Missing layers. Docker applies an AppArmor profile (docker-default) on this machine [M: docker info lists apparmor]. doos installs no AppArmor or SELinux profile of its own; its only profile exists to permit user namespaces on Ubuntu 24.04+ [D].
  • Supply chain. doos pins publisher signing keys for Alpine and Ubuntu and checks digests for Debian [D][M]. Registry-based tools rely on registry trust, content trust or external signing tools [K].

Full list of what doos does not cover is at the end of RESEARCH; the exact hardening is in SPEC under Hardening.

7. What doos lacks, and which code would change

These are the gaps that matter when comparing, each next to where it lives. They are descriptions, not promises.

Gap Where
No OCI registry pull, no layers, no build src/image.cpp (Catalog, ImageStore)
No host directory bind mounts, no devices, no GPU setupRoot in src/sandbox.cpp, option parsing in src/cli.cpp
No background containers, stop, exec by name, logs lifetime model in Sandbox::start / Sandbox::watch (container lives with its first command)
No user-defined networks, no -p src/network.cpp
No Compose or Kubernetes integration not present
Environment, user, working-directory flags missing applyLimitsAndEnv in src/sandbox.cpp
Linux x86_64 tested only src/hardening.cpp guards aarch64, never run there
seccomp is a denylist deniedSyscalls in src/hardening.cpp
Test coverage: one machine tests/run.sh

8. Conclusion

doos is not a Docker replacement and does not try to be. It is a small, rootless, daemonless tool for disposable or per-user Linux shells and sandboxes that start in tens of milliseconds, with safe defaults and hard caps already set. Its strengths are the ones the measurements show: it is a 552 KiB single binary, it starts in 14 to 65 ms [M], it runs with no daemon and no root, it caps memory, processes, CPU and written data by default, and it verifies what it downloads.

Choose by the job:

If you need Use
A quick, isolated shell or script runner for one user, host tools or a distro image, with limits, no daemon doos
OCI images, Dockerfiles or Containerfiles, registries, CI that matches production Podman (rootless, daemonless) or Docker
Compose, Kubernetes parity, Docker ecosystem tooling Docker or Podman with its Docker compatibility
Sandbox one application with exact control over mounts bubblewrap
A full OS with init and several services, long lived Incus / LXC or systemd-nspawn
Shared HPC cluster, MPI, GPUs, signed single-file images Apptainer
A desktop environment tied to your home directory Distrobox / Toolbx
Running code you do not trust at all, or multi-tenant workloads Kata / Firecracker (or gVisor as a middle step), not any shared-kernel container

Honest limits of this comparison: one machine, 10-run averages, no rootless Docker, no systemd-nspawn, Incus or Apptainer measurements, and several Docker and Podman capabilities listed as general knowledge rather than re-verified. The conclusion about when to use doos rests on what the doos tests and measurements show (RESEARCH), not on a claim that it beats mature tools on features. On features it does not.

Back to README for usage, SPEC for the exact behavior, ARCHITECTURE for how it works, AGENTS for the rules, and CHANGELOG for history.