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
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
slirp4netnsport driver - Then the published-port host is the child address it reports
- Given the
builtindriver, 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/infoanswers as the test assumes, and10.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, withnetwork: hostand on the bridge network alike - And an entry in
docker.envstill wins
Verified by
- DockerBuildRunnerTest — both network modes, and
docker.envwinning
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.mdand the architecture skill describe how the variable is set.- Version 1.25.0 with its release note.
Follow-up PRs
- vm4006: remove
TESTCONTAINERS_HOST_OVERRIDEfrom the machine config once this runs there. - hsadmin-ng:
tools/smoke-testreadsTESTCONTAINERS_HOST_OVERRIDEfor its own database.