From 7adf43dd0322ad06071560be75335023fa74ee0a Mon Sep 17 00:00:00 2001 From: retoor Date: Sat, 24 Oct 2026 07:35:30 +0200 Subject: [PATCH] Add --mount for host-dir bind mounts (e.g. a WebDAV share via rclone) Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 6 ++- COMPARISON.md | 38 ++++++------- README.md | 3 ++ SPEC.md | 6 ++- VERSION | 2 +- docs/ARCHITECTURE.md | 4 +- docs/RESEARCH.md | 3 +- docs/charts/bench.json | 116 +++------------------------------------- docs/charts/cpu.svg | 28 +++++----- docs/charts/size.svg | 6 +-- docs/charts/startup.svg | 80 +++++++++++++-------------- src/cli.cpp | 35 +++++++++++- src/container.cpp | 59 +++++++++++++++++++- src/container.hpp | 14 +++++ src/sandbox.cpp | 15 ++++++ src/spec.hpp | 1 + tests/run.sh | 15 ++++++ 17 files changed, 232 insertions(+), 199 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f2bb63..4dceb3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ **Docs:** [README](README.md) · [SPEC](SPEC.md) · [ARCHITECTURE](docs/ARCHITECTURE.md) · [RESEARCH](docs/RESEARCH.md) · [COMPARISON](COMPARISON.md) · [AGENTS](AGENTS.md) · [CHANGELOG](CHANGELOG.md) · [CI](docs/CI.md) -## 1.3.2 - 2026-10-24 +## 1.5.0 - 2026-10-24 +- `--mount SRC:DST[:ro]` (repeatable, max 8): bind-mounts a host directory into the container, outside the `--size` cap. Lets a container reach storage that is not local, for example a WebDAV share mounted on the host first with `rclone mount` or `davfs2`; doos never speaks a network filesystem protocol itself, it only bind-mounts what the host already mounted. Changeable like `--memory`/`--pids`/`--cpus`: replaces the whole mount list, applies at the next start. Tracked as gap G12 in [RESEARCH](docs/RESEARCH.md); 5 new end-to-end checks (read, write, read-only, missing source rejected, too many rejected). +- Docs: [COMPARISON](COMPARISON.md) section 1 and the gaps list updated (doos now has host-dir bind mounts); charts and generated numbers re-measured (`make bench && make charts`). + +## 1.4.0 - 2026-10-24 - CI: `.gitea/workflows/ci.yml` (Gitea Actions) runs `build`, `valgrind`, `test` and `audit` on every push. `build`/`valgrind` run on any Docker-mode runner; `test`/`audit` need a self-hosted host-mode runner (real namespaces, cgroup v2 delegation, the AppArmor profile), documented with its one-time setup in the new [docs/CI.md](docs/CI.md). Every step wraps its own `timeout`, since Gitea Actions parses but does not enforce `timeout-minutes`/`concurrency` (checked against the 1.25 docs). - Fixed, from [review2.md](review2.md): `parseSize` (`src/util.cpp`) overflowed silently on an oversized input (`--size 16777217t` was accepted as exactly `1.0T` instead of rejected); now checked before the shift and rejected. Tracked as gap G9 in [RESEARCH](docs/RESEARCH.md); regression case added to `tests/run.sh`. - Fixed, from [review2.md](review2.md): `newuidmap`, `newgidmap`, `slirp4netns` and `pasta` were each pinned to one hardcoded `/usr/bin/...` path; they now go through `resolveBinary()` (state-dir override, then a fixed list of system directories), the same resolution `curl`/`tar`/`sha256sum`/`gpg` already used. Tracked as gap G10. diff --git a/COMPARISON.md b/COMPARISON.md index e667f83..1e6f716 100644 --- a/COMPARISON.md +++ b/COMPARISON.md @@ -22,7 +22,7 @@ Versions compared: doos 1.1.1, Docker 29.1.3 (rootful daemon on this machine), P | Who can use it safely | any user | only trusted users: access to the daemon is root-equivalent [S: [Docker security](https://docs.docker.com/engine/security/), [iximiuz](https://labs.iximiuz.com/tutorials/docker-security-introduction-a859718d)] | 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 | +| Host dirs into the container (`-v`) | yes, `--mount SRC:DST[:ro]`, not subject to the size cap [D] | yes [K] | yes | yes | | Published ports | none for `slirp`; automatic for `pasta` [D] | `-p` [K] | `-p` | `-p` [S: [podman-rootless(7)](https://man.archlinux.org/man/podman-rootless.7.en)] | | Rootless network backend | slirp4netns or pasta [D] | n/a | RootlessKit based [S: [Podman blog](https://blog.podman.io/2023/07/podman-performance-root-and-rootless/)] | pasta default since 5.0 [S: [podman-rootless(7)](https://man.archlinux.org/man/podman-rootless.7.en)] | | Resource limits | memory, pids, cpus, written data [D][M] | memory, cpu, pids, more [K] | needs cgroup v2 + systemd delegation [S: [Docker rootless tips](https://docs.docker.com/engine/security/rootless/tips/)] | needs cgroup v2 [S: [podman-rootless(7)](https://man.archlinux.org/man/podman-rootless.7.en)] | @@ -30,7 +30,7 @@ Versions compared: doos 1.1.1, Docker 29.1.3 (rootful daemon on this machine), P | Default capabilities | Docker's default set [D][M] | that set [S] | that set | a smaller set [S: [dev.to comparison](https://dev.to/mechcloud_academy/docker-vs-podman-an-in-depth-comparison-2025-2eia)] | | 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.2 MiB [M] | +| Binary size | 532 KiB, 6 shared libraries, 3,712 lines of C++ [M] | CLI 29.9 MiB, plus `dockerd` and `containerd` [M][K] | same | 41.2 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 | @@ -48,24 +48,24 @@ Versions compared: doos 1.1.1, Docker 29.1.3 (rootful daemon on this machine), P ## 3. Measurements -Method: a trivial command (`true`) started 10 times in a row after one warm-up run, average wall time per run, the Alpine image already present for every tool, one machine, one run set on 2026-10-23. Versions: doos 1.1.2, Docker 29.1.3, Podman 5.7.0, bubblewrap 0.11.1. The numbers come from `scripts/bench.sh` (`make bench`); the charts and the tables below are generated from its output (`make charts`), so they cannot drift from each other. +Method: a trivial command (`true`) started 10 times in a row after one warm-up run, average wall time per run, the Alpine image already present for every tool, one machine, one run set on 2026-10-24. Versions: doos 1.4.0, Docker 29.1.3, Podman 5.7.0, bubblewrap 0.11.1. The numbers come from `scripts/bench.sh` (`make bench`); the charts and the tables below are generated from its output (`make charts`), so they cannot drift from each other. ![Horizontal bar chart of the time to start a trivial command for doos, bubblewrap, Podman and Docker, with and without network. doos is highlighted. Exact values are in the table below.](docs/charts/startup.svg) | Setup | Network | Time per run | |---|---|---| -| bubblewrap (host /usr) | none | 8.4 ms | -| doos Alpine image | none | 13.8 ms | -| doos host mode | none | 18.7 ms | -| Podman Alpine | none | 208 ms | -| Docker Alpine (rootful) | none | 344 ms | -| bubblewrap (host /usr) | default | 7.4 ms | -| doos Alpine image | default | 52.1 ms | -| doos host mode | default | 56.9 ms | -| doos Alpine, pasta | default | 64.6 ms | -| Podman Alpine | default | 241 ms | -| Docker Alpine (rootful) | default | 436 ms | +| bubblewrap (host /usr) | none | 8.8 ms | +| doos Alpine image | none | 14.2 ms | +| doos host mode | none | 20.2 ms | +| Podman Alpine | none | 283 ms | +| Docker Alpine (rootful) | none | 463 ms | +| bubblewrap (host /usr) | default | 7.8 ms | +| doos Alpine image | default | 58 ms | +| doos host mode | default | 61.4 ms | +| doos Alpine, pasta | default | 82.5 ms | +| Podman Alpine | default | 313 ms | +| Docker Alpine (rootful) | default | 645 ms | How to read this honestly: @@ -82,7 +82,7 @@ How to read this honestly: |---|---| | bubblewrap | 79 KiB (80,424 bytes) | | crun | 472 KiB (483,392 bytes) | -| doos | 552 KiB (565,680 bytes) | +| doos | 532 KiB (544,640 bytes) | | docker CLI | 29.9 MiB (31,374,336 bytes) | | podman | 41.2 MiB (43,180,896 bytes) | @@ -105,8 +105,8 @@ The Podman figure is this machine's accumulated state, not a like-for-like compa **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 of C++ [M], one process tree, no background service, nothing listening. -- Instant: 14 to 65 ms against 344 to 436 ms for a rootful Docker run and 208 to 241 ms for Podman [M]. +- Tiny and auditable: 532 KiB, 3,712 lines of C++ [M], one process tree, no background service, nothing listening. +- Instant: 14 to 82 ms against 463 to 645 ms for a rootful Docker run and 283 to 313 ms for Podman [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](https://fabianlee.org/2020/01/15/docker-use-overlay2-with-an-xfs-backing-filesystem-to-limit-rootfs-size/)]. @@ -175,7 +175,7 @@ These are the gaps that matter when comparing, each next to where it lives. They | 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 devices, no GPU passthrough (host directory bind mounts exist: `--mount`) | `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 | @@ -186,7 +186,7 @@ These are the gaps that matter when comparing, each next to where it lives. They ## 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. +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 532 KiB single binary, it starts in 14 to 82 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: diff --git a/README.md b/README.md index 4647c5f..d0901ea 100644 --- a/README.md +++ b/README.md @@ -114,9 +114,12 @@ Notes, all measured: | `--memory S` | memory cap | 8g | changeable | | `--pids N` | process/thread cap | 4096 | changeable | | `--cpus N` | cpu cap, e.g. `0.5`, `2` | all cores | changeable | +| `--mount SRC:DST[:ro]` | bind-mount host dir `SRC` at `DST` inside, repeatable (max 8) | none | changeable | Changeable options apply the next time the container starts. Examples: `doos attach --memory 2g --cpus 1 build`, `doos attach --net pasta --image debian-bookworm web`. +`--mount` gives a container extra storage without counting against `--size`: mount a remote filesystem on the host first (for example a WebDAV share with `rclone mount webdav-remote: /mnt/webdav`), then `doos attach --mount /mnt/webdav:/data NAME` to reach it inside. `SRC` must exist and be a directory when the container starts; it fails loudly otherwise. `--mount SRC:DST:ro` mounts it read-only. + ## Limits, size cap and hardening - **Limits** (`--memory`, `--pids`, `--cpus`): cgroup v2, measured here: a 256m container touching 400 MB is killed whole and doos says so; 80 forks against `--pids 32` fail and are reported; `--cpus 0.5` held 4 busy loops to 2.3 CPU-seconds in 4.3 s of wall time. diff --git a/SPEC.md b/SPEC.md index f3503b5..5272a2a 100644 --- a/SPEC.md +++ b/SPEC.md @@ -32,8 +32,9 @@ Container options (accepted by `get_or_create` and `attach`): | `--memory SIZE` | Memory cap (cgroup `memory.max`, swap 0, whole-container OOM kill). | 8g | yes | | `--pids N` | Process and thread cap (`pids.max`). | 4096 | yes | | `--cpus N` | CPU cap, decimals allowed (`0.5`, `2`); `0` = all cores. | 0 | yes | +| `--mount SRC:DST[:ro]` | Bind-mount host directory `SRC` at `DST` inside the container, repeatable (max 8). Not subject to `--size`: it is host storage, not the container's writable layer. Replaces the whole mount list when given. | none | yes | -Changeable options apply at the next start of the container. Naming a fixed option on an existing container with a different value is an error, never a silent change. +Changeable options apply at the next start of the container. Naming a fixed option on an existing container with a different value is an error, never a silent change. `--mount`'s `SRC` must exist and be a directory at the time the container starts (host responsibility, e.g. `rclone mount` a WebDAV share there first); a missing `SRC` fails the start loudly. ## Limits (hard caps) @@ -59,6 +60,7 @@ Changeable options apply at the next start of the container. Naming a fixed opti | slirp4netns ready wait | 5 s | | Lock wait | 5 s | | Tree recursion depth | 256 | +| `--mount` entries | 8 | ## State layout @@ -69,7 +71,7 @@ containers/NAME/{meta,pid,lock,seeded.*,rootfs/,layers//{upper,work},privat images/NAME/{meta,rootfs/} ``` -`meta` keys: `net created version image ids size`. +`meta` keys: `net created version image ids size memory pids cpus hardening mounts`. ## Container filesystem, host mode (no `--image`) diff --git a/VERSION b/VERSION index 88c5fb8..bc80560 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.0 +1.5.0 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index d821583..ff21e15 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -71,7 +71,9 @@ Image mode: 2. Mountpoints (`/proc /dev /tmp /run ...`) created inside the merged tree. Both modes then: tmpfs `/tmp` and `/run`; `/dev` (tmpfs, bound host nodes, new devpts, `/dev/shm`, symlinks); -`/proc` with read-only remounts and masks from `spec.hpp`; `chdir(rootfs)`, `pivot_root(".", ".")`, lazy +`/proc` with read-only remounts and masks from `spec.hpp`; `mountExtra` binds each `Container::mounts()` +entry (`--mount SRC:DST[:ro]`) at `DST`, creating the mountpoint first (`fsutil::mkdirs`), read-only ones +remounted the same way `/proc`'s read-only paths are; `chdir(rootfs)`, `pivot_root(".", ".")`, lazy unmount of the old root. ## Seeding (host mode, once per container) diff --git a/docs/RESEARCH.md b/docs/RESEARCH.md index 71807d2..2a5115f 100644 --- a/docs/RESEARCH.md +++ b/docs/RESEARCH.md @@ -66,6 +66,7 @@ removed afterwards. Rows marked "unmeasured" have no run behind them. | G9 | `parseSize` left-shifted without an overflow check: `--size 16777217t` wrapped to exactly `1.0T` and was accepted instead of rejected | check `v > (UINT64_MAX >> shift)` before shifting, reject instead of wrapping | **done in 1.3.2**, reproduced before the fix, re-run after: rejected | `parseSize` in `src/util.cpp` | | G10 | `newuidmap`/`newgidmap`/`slirp4netns`/`pasta` were pinned to a single hardcoded `/usr/bin/...` path, unlike `curl`/`tar`/`sha256sum`/`gpg` which go through `resolveBinary()`'s state-dir-override-then-fixed-dirs search; a distro installing them elsewhere would fail even though PATH-poisoning was never possible either way | route all four through `resolveBinary()`, same as the other helpers | **done in 1.3.2** | `idmap.cpp`, `network.cpp` | | G11 | Alpine's `latest-releases.yaml` and Debian's `index.json` (embedded base64 manifest) are read with fixed string/byte-offset search, not a YAML/JSON library; a publisher reformatting either file breaks extraction with a thrown error (fails closed) rather than silently misreading it | none: a dependency to parse two small, publisher-controlled files is a worse trade than failing loudly when their format changes, given the project's own "fewer dependencies" rule | **accepted, not fixed**; this row exists so the trade-off is written down, not rediscovered | `Catalog::resolve` in `src/image.cpp` | +| G12 | No way to give a container storage that does not count against `--size` (remote/network storage, e.g. a WebDAV share); every writable path was either the host overlay (capped) or a private dir (capped) | `--mount SRC:DST[:ro]`: bind-mount an already-mounted host path into the container, outside the size watchdog's scan set (`Container::usedBytes` only scans `layers/`/`private/`, never `--mount` targets). doos itself never speaks a network filesystem protocol: mount WebDAV/NFS/etc. on the host first (e.g. `rclone mount`), doos only bind-mounts what is already there | **done in 1.5.0** (`--mount`, max 8 per container, changeable, applies at next start); missing `SRC` fails the start loudly | `Container::mounts`, `mountExtra` in `src/sandbox.cpp` | ### G1: memory, cpu and pids limits (cgroup v2) @@ -271,7 +272,7 @@ delegated with `cpuset cpu io memory pids`). The end-to-end suite passed 83 chec ![Horizontal bar chart of CPU-seconds used by four busy loops in one container, without a limit and with --cpus 0.5. The limited container is highlighted. Values are in the sentence below.](charts/cpu.svg) -The chart above is generated from `docs/charts/bench.json` (`make bench`, then `make charts`); it shows 16.3 CPU-seconds without a limit and 2.3 with `--cpus 0.5`; the startup and size charts live in [COMPARISON](../COMPARISON.md). +The chart above is generated from `docs/charts/bench.json` (`make bench`, then `make charts`); it shows 15.6 CPU-seconds without a limit and 2.4 with `--cpus 0.5`; the startup and size charts live in [COMPARISON](../COMPARISON.md). ### What changed in behavior (and why it is not a loss) diff --git a/docs/charts/bench.json b/docs/charts/bench.json index 704c67a..c276041 100644 --- a/docs/charts/bench.json +++ b/docs/charts/bench.json @@ -1,121 +1,17 @@ { "meta": { - "date": "2026-10-23", + "date": "2026-10-24", "kernel": "7.0.0-38-generic", "cpus": 12, "runs": 10, - "doos": "1.1.2", + "doos": "1.4.0", "podman": "podman version 5.7.0", "docker": "Docker version 29.1.3, build 29.1.3-0ubuntu4.1", "bubblewrap": "bubblewrap 0.11.1", - "src_lines": 3445, + "src_lines": 3712, "shared_libs": 6 }, - "startup": [ - { - "id": "bwrap_none", - "label": "bubblewrap (host /usr)", - "group": "none", - "kind": "other", - "ms": 8.4 - }, - { - "id": "bwrap_net", - "label": "bubblewrap (host /usr)", - "group": "default", - "kind": "other", - "ms": 7.4 - }, - { - "id": "doos_host_none", - "label": "doos host mode", - "group": "none", - "kind": "doos", - "ms": 18.7 - }, - { - "id": "doos_alpine_none", - "label": "doos Alpine image", - "group": "none", - "kind": "doos", - "ms": 13.8 - }, - { - "id": "doos_host_slirp", - "label": "doos host mode", - "group": "default", - "kind": "doos", - "ms": 56.9 - }, - { - "id": "doos_alpine_slirp", - "label": "doos Alpine image", - "group": "default", - "kind": "doos", - "ms": 52.1 - }, - { - "id": "doos_alpine_pasta", - "label": "doos Alpine, pasta", - "group": "default", - "kind": "doos", - "ms": 64.6 - }, - { - "id": "podman_none", - "label": "Podman Alpine", - "group": "none", - "kind": "other", - "ms": 207.8 - }, - { - "id": "podman_net", - "label": "Podman Alpine", - "group": "default", - "kind": "other", - "ms": 241.2 - }, - { - "id": "docker_none", - "label": "Docker Alpine (rootful)", - "group": "none", - "kind": "other", - "ms": 343.9 - }, - { - "id": "docker_net", - "label": "Docker Alpine (rootful)", - "group": "default", - "kind": "other", - "ms": 435.6 - } - ], - "sizes": [ - { - "id": "bwrap", - "bytes": 80424 - }, - { - "id": "crun", - "bytes": 483392 - }, - { - "id": "doos", - "bytes": 565680 - }, - { - "id": "docker", - "bytes": 31374336 - }, - { - "id": "podman", - "bytes": 43180896 - } - ], - "cpu": { - "wall_seconds": 4.2, - "busy_loops": 4, - "unlimited": 16.26, - "half_cpu": 2.29 - } + "startup": [{"id":"bwrap_none","label":"bubblewrap (host /usr)","group":"none","kind":"other","ms":8.8},{"id":"bwrap_net","label":"bubblewrap (host /usr)","group":"default","kind":"other","ms":7.8},{"id":"doos_host_none","label":"doos host mode","group":"none","kind":"doos","ms":20.2},{"id":"doos_alpine_none","label":"doos Alpine image","group":"none","kind":"doos","ms":14.2},{"id":"doos_host_slirp","label":"doos host mode","group":"default","kind":"doos","ms":61.4},{"id":"doos_alpine_slirp","label":"doos Alpine image","group":"default","kind":"doos","ms":58.0},{"id":"doos_alpine_pasta","label":"doos Alpine, pasta","group":"default","kind":"doos","ms":82.5},{"id":"podman_none","label":"Podman Alpine","group":"none","kind":"other","ms":282.9},{"id":"podman_net","label":"Podman Alpine","group":"default","kind":"other","ms":313.2},{"id":"docker_none","label":"Docker Alpine (rootful)","group":"none","kind":"other","ms":462.6},{"id":"docker_net","label":"Docker Alpine (rootful)","group":"default","kind":"other","ms":645.1}], + "sizes": [{"id":"bwrap","bytes":80424},{"id":"crun","bytes":483392},{"id":"doos","bytes":544640},{"id":"docker","bytes":31374336},{"id":"podman","bytes":43180896}], + "cpu": {"wall_seconds": 4.2, "busy_loops": 4, "unlimited": 15.56, "half_cpu": 2.38} } diff --git a/docs/charts/cpu.svg b/docs/charts/cpu.svg index 75d7ff5..bf33a83 100644 --- a/docs/charts/cpu.svg +++ b/docs/charts/cpu.svg @@ -1,6 +1,6 @@ CPU time used by four busy loops -Two bars, CPU seconds consumed in 4.2 seconds of wall time. Without a limit 16.3, with --cpus 0.5 2.3. +Two bars, CPU seconds consumed in 4.2 seconds of wall time. Without a limit 15.6, with --cpus 0.5 2.4. CPU time used by 4 busy loops in one container (CPU-seconds) -4.2 s of wall time. Half a CPU allows about 2.1 CPU-seconds; measured 2.3. +4.2 s of wall time. Half a CPU allows about 2.1 CPU-seconds; measured 2.4. no limit --cpus 0.5 0 - -4 - -8 - -12 - -16 + +4 + +8 + +12 -20 +16 no limit -no limit: 16.3 CPU-seconds -16.3 CPU-s +no limit: 15.6 CPU-seconds +15.6 CPU-s --cpus 0.5 ---cpus 0.5: 2.3 CPU-seconds -2.3 CPU-s +--cpus 0.5: 2.4 CPU-seconds +2.4 CPU-s diff --git a/docs/charts/size.svg b/docs/charts/size.svg index 8085341..91ae219 100644 --- a/docs/charts/size.svg +++ b/docs/charts/size.svg @@ -1,6 +1,6 @@ Size of the main executable -Horizontal bars on a logarithmic axis: bubblewrap 79 KiB, crun (OCI runtime) 472 KiB, doos 552 KiB, docker CLI 29.9 MiB, podman 41.2 MiB. +Horizontal bars on a logarithmic axis: bubblewrap 79 KiB, crun (OCI runtime) 472 KiB, doos 532 KiB, docker CLI 29.9 MiB, podman 41.2 MiB.