Files
werkator/docs/prs/2026-10-01-PR#86-rootlesskit-host-override.md
b37855e04a
werkator build succeeded after 03:40
werkator/deploy build succeeded after 00:07
feat(docker): ask rootlesskit where a rootless daemon publishes ports (#86)
With the slirp4netns port driver a rootless daemon publishes ports on
its child address only, not on localhost. Werkator now asks rootlesskit
(GET /v1/info on dockerd-rootless/api.sock) before every Docker build
and passes that address as TESTCONTAINERS_HOST_OVERRIDE, in both network
modes; the builtin driver keeps the defaults. A daemon switched between
drivers needs no configuration change. docker.env still wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: mhoennig <michael@hoennig.de>
Reviewed-on: #86
2026-10-01 11:59:07 +02:00

3.4 KiB

WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.

The Problem

On vm4006 the rootless Docker daemon switched from the builtin to the slirp4netns port driver at a reboot on 2026-10-01; the host's configuration management had rewritten its unit a week before. With slirp4netns, published ports listen only on the child address of the daemon's network namespace, 10.0.2.100, not on localhost. Every Testcontainers test failed with "connection refused". PR#85 let the host write the address into docker.env. But the address is rootlesskit's default, not a constant, and the driver can change back just as unannounced; each change would need a hand-edited configuration again.

Non-Goals

  • No support for rootful daemons with unusual port setups; their defaults stay.
  • No change to how a build script inside the container finds the address; it reads TESTCONTAINERS_HOST_OVERRIDE.

The Scenarios

Feature: Werkator asks the daemon where it publishes ports

Scenario#86.01: rootlesskit names the address

So that the address follows the daemon, both ways, without a configuration change.

  • Given a rootless daemon whose rootlesskit API reports the slirp4netns port driver
  • Then the published-port host is the child address it reports
  • Given the builtin driver, a rootful daemon, a missing API or an unreadable answer
  • Then there is none, and the defaults apply
Verified by
  • RootlessKitPortsTest — a real HTTP exchange over a Unix socket with vm4006's answer, the builtin driver, rootful, missing and unreadable cases
  • By hand on vm4006: curl --unix-socket /run/user/10000/dockerd-rootless/api.sock http://rootlesskit/v1/info answers as the test assumes, and 10.0.2.100:<port> is reachable from a host-network and from a bridge container, host.docker.internal:<port> is not.

Scenario#86.02: The build gets it as TESTCONTAINERS_HOST_OVERRIDE

  • Given a published-port host from rootlesskit
  • Then the build container gets it as TESTCONTAINERS_HOST_OVERRIDE, with network: host and on the bridge network alike
  • And an entry in docker.env still wins
Verified by

The Solution

RootlessKitPorts sends GET /v1/info to dockerd-rootless/api.sock next to the daemon socket, over a Unix domain socket channel, with a two-second limit. It takes networkDriver.childIP when portDriver.driver is slirp4netns and the address is IPv4, and logs a change of state once. DockerBuildRunner asks it on every build, for the located socket whether or not the build mounts it. No curl is needed on the host, and nothing is cached: a driver change takes effect at the next build.

Additional Changes

  • docs/configuration.md and the architecture skill describe how the variable is set.
  • Version 1.25.0 with its release note.

Follow-up PRs

  • vm4006: remove TESTCONTAINERS_HOST_OVERRIDE from the machine config once this runs there.
  • hsadmin-ng: tools/smoke-test reads TESTCONTAINERS_HOST_OVERRIDE for its own database.