fix: isolate runner job networks and pin cache.host so concurrent jobs stop sharing service container aliases #1
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The Forgejo runner at
~/Development/forgejo(separate repo, shared production infra) setscontainer.network: "forgejo-internal"inrunner-config/config.yml— a named, long-lived bridge — together withrunner.capacity: 2. Because the network is named rather than empty, the runner attaches every job container and every service container to one shared network instead of creating a per-job network.Forgejo's own admin documentation calls out this exact combination:
Two distinct consequences:
Reliability. Concurrent jobs whose service containers share an alias get round-robined between each other's databases. This is the root cause of kirria#205. That ticket works around it inside
kirria/.forgejo/workflows/ci.ymlby giving Postgres a run-scoped unique name; this ticket is the platform-level fix, which would benefit every repo on the instance rather than just kirria.Security. Job containers currently share a network with Forgejo's own production
dband mail containers. Forgejo's security documentation flags this as an access-boundary weakness independent of any reliability concern — arbitrary workflow code on this instance sits on the same bridge as the production database.Plan
Two lines, not one. Clearing
container.networkalone will likely break the runner's built-in cache server.runner-config/config.yml— setcontainer.network: ""so the runner auto-creates an isolated per-job network (forgejo-runner's documented default).runner-config/config.yml— pincache.hostto a host-reachable address. It is currently""(auto-detect). forgejo-runner'sconfig.example.yamlwarns:Per-job networks are precisely the condition that breaks auto-detect. Because
docker_host: "automount"means runner and spawned job containers are siblings against the same host daemon, a bridge-network container can always reach the host's IP even when isolated from other bridges — so the host's stable LAN or docker0-gateway IP is the right value. Leaveproxy_port: 0(auto-select).Runner→Forgejo (
http://forgejo:3000) is unaffected. That connection is made by theforgejo-kirria-org-ci-runnercontainer itself for polling, registration, log upload, and status reporting; its network placement is fixed indocker-compose.yml(networks: - forgejo-internal) and is not governed bycontainer.network, which only applies to the ephemeral containers the runner spawns. Job containers never talk to Forgejo over the internal bridge —actions/checkoutclones over the publicROOT_URLvia normal outbound NAT.Context
actions/cachesteps specifically. Cache failure is the predicted failure mode ifcache.hostis wrong.runner-config/config.ymlvia a read-only bind mount:deploy-runner-1,deploy-runner-2,kirria-org-runner,kirria-org-ci-runner. A change here hits all four at once, which is an argument for the canary.cache.hosttuning goes sideways here.Out of scope
History: why the setting exists
Traced the provenance, which changes the risk assessment.
5a29733 "add network"(Dec 14 21:24) flippednetwork: ""->"forgejo-internal", five minutes afterddc97ef "rename the runners"(21:19). At that point every runner ran against a separate dind daemon:78beee3 "take runners out of dind"later moved the runners onto the host socket (docker_host: automount, noDOCKER_HOST). The network setting was never revisited after that move. The upstream explanatory comments were also stripped fromconfig.ymlin a later rewrite, which is why nothing in the file explains the choice today.Implication: the setting most likely solved a dind-era reachability problem that no longer exists. That lowers the expected risk of reverting it, but does not remove the need for a canary — one of the original justifications may still bind.
Correction to the Plan above
The Plan claims job containers never talk to Forgejo over the internal bridge, on the grounds that
actions/checkoutclones over the publicROOT_URL. That is asserted, not verified, and the whole ticket rests on it.The runners register with
GITEA_INSTANCE_URL=http://forgejo:3000— an internal-only name. Whether a job container'sactions/checkoutderives its clone URL from Forgejo's configured publicROOT_URLor from the runner's registration URL decides the outcome:ROOT_URL: clearing the network is safe, outbound NAT handles it.forgejo, and checkout breaks for every repo on the instance.Verify before or during the canary. The failure mode is total, not partial — watch the checkout step, not just
actions/cache.Whichever way it resolves, add a comment to
config.ymlrecording the decision. Its absence is why this took a git-archaeology session to reconstruct.