Sign inSign up

mwaeckerlin/dockindock

By mwaeckerlin

•Updated about 4 hours ago

Image
0

2.5K

mwaeckerlin/dockindock repository overview

⁠Rootless Docker in Docker

mwaeckerlin/dockindock⁠ runs a complete docker daemon inside a container — as an unprivileged user. Use it for CI jobs, sandboxes and nested tooling that need their own isolated docker without handing them root.

Unlike classic docker-in-docker images, the inner daemon runs rootless: even a full compromise of the inner docker never yields a root process inside the container. The docker CLI is preconfigured (DOCKER_HOST, PATH), so docker … works out of the box.

All features are listed in FEATURES.md⁠, all tests in TESTS.md⁠.

The image is self-contained: it needs no host configuration (no AppArmor profile, no sysctl change) — current Ubuntu hosts restrict user-namespace creation for capability-less processes, and the image answers that with a CAP_SYS_ADMIN file capability on the small rootlesskit supervisor (see the trade-off below).

⁠Usage

The container must be started with --privileged (rootless docker needs the namespace and mount privileges):

docker run -d --privileged --name dockindock mwaeckerlin/dockindock

Or with the bundled compose file:

  • npm run build
  • npm start (foreground) or npm run start:daemon (background)
  • run a container inside: docker compose exec dockindock docker run --rm alpine echo hello
  • stop with Ctrl+C (or npm stop for daemon mode)

⁠Configuration

  • DOCKER_DATA_ROOT (default /docker-data) — where the inner daemon stores images and containers. Always mount a named volume on /docker-data: besides persistence, the inner overlay mounts cannot sit on the container's own overlayfs (the kernel rejects overlay-on-overlay). Never mount over /docker (that would mask the toolchain).
  • DOCKER_TCP_PORT (default: unset) — when set (e.g. 2375), the docker API is additionally published on tcp://0.0.0.0:<port>, so the container can serve as a shared docker-in-docker service for sibling containers that point DOCKER_HOST=tcp://<service>:<port> at it. Malformed values abort startup with a clear error.

⁠Security Trade-Offs

  • --privileged is required: the flag grants the container broad access to the host — run the image only on hosts where that is acceptable. Inside the container, everything still runs as an unprivileged user (rootless daemon), which is the whole point of this image.
  • The rootlesskit supervisor carries a CAP_SYS_ADMIN file capability: current Ubuntu hosts block user-namespace creation for capability-less processes; the capability is the documented exemption and keeps the image independent of any host configuration. Only the small supervisor gains it — inside a container that must be --privileged anyway — while the daemon and every workload keep running as the unprivileged user.
  • The toolchain directory /docker/bin precedes the system PATH: the rootless binaries exist nowhere else, and the directory belongs to the same unprivileged user the daemon runs as — a process able to write there already runs as that user, so nothing is gained by shadowing.
  • The optional TCP endpoint is plain, unauthenticated TCP: anyone who can reach the port controls the inner daemon. Because the daemon is rootless, that grants at most the unprivileged daemon user — still, only enable DOCKER_TCP_PORT on an isolated container network dedicated to the consumers (as the hermes and openclaw stacks do), never publish it to a host port or a shared network.

⁠Tests

npm test runs the docs contract (every feature in FEATURES.md⁠ has a test in TESTS.md⁠, no skipped tests), the config contract (client, rootless launcher, subordinate ids, unprivileged user, preconfigured environment) and the end to end suite, which starts the daemon privileged and runs a real inner container:

npm test            # docs + config contract + e2e

⁠Development

The image is built from mwaeckerlin/very-base⁠ (build stage) and ships on mwaeckerlin/scratch⁠; the rootless toolchain is installed by the official get.docker.com/rootless script into /docker.

Tag summary

Content type

Image

Digest

sha256:2c8275e7d…

Size

115.3 MB

Last updated

about 4 hours ago

docker pull mwaeckerlin/dockindock