Skip to main content

ADR: Container Runtime Tradeoffs — Docker (Rootful), Docker (Rootless), and Podman

Context

Selecting a container runtime for baremetal Docker hosts involves balancing security, operational convenience, and tooling compatibility. This ADR breaks down three approaches and the fundamental tension between central-socket management and daemonless isolation.

The host OS decision is handled in ADR 0002: Baremetal Docker Host OS Selection.

Runtime Options

Docker (Rootful)

The classic model: dockerd runs as root, exposes /var/run/docker.sock.

AspectDetail
Socket/var/run/docker.sock — single, system-wide, owned by root:docker
Daemondockerd running as root — always-on, manages all containers
Root modelContainer processes run as root by default (can drop to user with USER in Dockerfile)
Komodo supportFirst-class — native socket access, all features work
Setup complexityMinimal — dnf install docker, start daemon, add user to docker group
ComposeNative docker compose v2 support

Docker (Rootless)

dockerd runs under a user namespace, exposes a user-scoped socket.

AspectDetail
Socket/run/user/$UID/docker.sock — per-user, still a single management endpoint
Daemondockerd-rootless running as the user — still a central daemon
Root modelContainers run in a user namespace — real root is not exposed, but root within the namespace
Komodo supportViable with configuration — DOCKER_HOST env pointed at rootless socket
Setup complexityModerate — dockerd-rootless-setuptool.sh, loginctl enable-linger, env setup
ComposeNative docker compose v2 — works within user namespace

Podman (Daemonless)

No central daemon. Each container is launched directly by the user’s Podman client.

AspectDetail
SocketNone by default — containers are child processes of the user’s shell
DaemonNo daemon — podman system service can emulate a Docker API socket on demand
Root modelRootless by default — no user namespace needed, containers run as the calling user
Komodo supportWorkarounds only — DOCKER_HOST to emulated socket, known bugs (stack status, network labels)
Setup complexityMinimal for CLI — dnf install podman. Complex for management tooling (need podman system service as a systemd unit)
Composepodman-compose or podman play kube — not drop-in compatible

Analysis

Security: The Central Socket Risk

A Docker socket is equivalent to root access on the host. Any process that can write to the socket can:

  • Start containers with host filesystem mounts (read/write any file)
  • Create privileged containers that break out of isolation
  • Modify iptables, network namespaces, cgroups
  • Access secrets mounted into any container

Rootful Docker places this power on a single, well-known path (/var/run/docker.sock). The docker group is effectively root. The blast radius of a compromised process with socket access is the entire host.

Rootless Docker mitigates this through user namespaces. The daemon and all containers run within the user’s namespace — root inside a container is the user outside. A compromised socket can’t escape the user namespace, so the blast radius is that user’s resources only. However, it’s still a central daemon — if the rootless dockerd process is exploited, all that user’s containers are compromised.

Podman eliminates the central socket entirely. No process management attachment, no daemon to target. Each container is a child of its launching process. The blast radius is per container — no socket to poison. To use management tooling, you must explicitly start a podman system service socket, which reintroduces a central endpoint — but it’s opt-in, not the default.

Management: Daemonless vs. Central Socket

This is the inverse of the security tradeoff:

RuntimeManagement modelTooling friction
Docker (rootful)One socket, one daemon, everything worksZero — every tool speaks Docker API natively
Docker (rootless)One socket per user, one daemon per userLow — set DOCKER_HOST, same API
PodmanNo socket, no daemon, containers are child processesHigher — no standard API without emulation layer

Without a central socket, management tooling faces a discovery problem:

  • How does Komodo’s Periphery find the container runtime? It can’t — it needs an explicit path to podman system service socket, which must be running as a systemd user service.
  • How does docker ps / podman ps enumerate running containers across a session? Podman uses a per-user boltdb database, but there’s no persistent daemon registering container state.
  • How do compose stacks get tracked? Podman’s compose support (podman-compose) uses a different project model than Docker Compose — Komodo’s stack status detection breaks (issue #504).

With a central socket, management is trivial: connect to the well-known path, issue standard API calls, get predictable results. The cost is the security risk described above.

Operational Considerations

CriterionDocker (rootful)Docker (rootless)Podman (daemonless)
Komodo compatibilityNativeConfigurableWorkarounds
Socket securityDangerous (root equiv.)Safer (user namespaced)None by default
Privileged containersFull supportBlockedBlocked (rootless)
Port <1024NativeWorkaround neededWorkaround needed
Systemd integrationManualManualNative (podman generate systemd)
cgroup managementFullLimitedLimited (rootless)
BuildsNativeNative (BuildKit)Native (Buildah)
Swarm modeSupportedBlockedN/A
Docker ComposeNativeNativePartial (podman-compose)
Multi-arch buildsNativeNativeNative (Buildah)

This ADR does not make a standalone decision — it informs ADR 0002. The current direction:

Docker (rootful) for maximum simplicity and tooling compatibility. The socket-security risk is accepted as an acceptable tradeoff for a homelab with trusted workloads.

Podman is the more secure architecture by design, and rootless Docker is a middle ground. Rootful Docker optimizes for operational simplicity — no setup ceremony, no port restrictions, no privileged container limitations. For a homelab where all workloads are user-deployed and trusted, the central-socket risk is manageable.

If the homelab expands to include untrusted or multi-tenant workloads, this should be revisited — rootless Docker or Podman would contain blast radius in a way rootful Docker cannot.

Consequences

If Docker (rootful) is adopted (current direction):

  • All management tooling works natively with zero configuration — standard Docker API on the well-known socket
  • Setup is minimal — dnf install docker, enable daemon, run containers
  • No workload restrictions — privileged containers, ports <1024, cgroup limits all work
  • Socket is root-equivalent — any process with socket access owns the host. Acceptable for homelab trusted workloads.

If Docker (rootless) or Podman is reconsidered later:

  • Stronger isolation — compromise is contained to a user namespace or individual container
  • Some workloads (privileged containers, Swarm mode) are restricted
  • Management complexity increases — socket discovery, env vars, emulation layers required for Komodo
  • Ports <1024 need workarounds

References