From 078ea74ba9b0a676db35b919437a6aad81c911d4 Mon Sep 17 00:00:00 2001 From: sock Date: Fri, 10 Jul 2026 17:30:25 +0200 Subject: [PATCH] fix(hive-ci): static bridge IP instead of DHCP (no DHCP server on the bridge) hive-ci is the one container on the hive bridge that used DHCP (networking.interfaces.eth0.useDHCP = true, from #2310 / #2336). But the bridge has no DHCP server: dnsmasq on the bridge is DNS-only and agent containers get deterministic static IPs (lifecycle::agent_network_ip), so the DHCP client never gets a lease and the container hangs at boot: A start job is running for DHCP Client (5s / 1min 31s) Assign a static address on eth0 (top host address of the subnet) plus a default route via the bridge gateway, and drop useDHCP. Preserves the #2310 netns isolation (no host-loopback reach) while letting the container boot and reach the forge through the gateway. Refs #2310, #2336. --- nix/modules/hive-ci.nix | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/nix/modules/hive-ci.nix b/nix/modules/hive-ci.nix index 96ddebdb..986d468b 100644 --- a/nix/modules/hive-ci.nix +++ b/nix/modules/hive-ci.nix @@ -11,6 +11,19 @@ let networkCfg = config.services.hyperhive.network; tlsCfg = config.services.hyperhive.tls; + # Static bridge address for the hive-ci container. The hive bridge has + # NO DHCP server: agent containers get deterministic static IPs + # (`lifecycle::agent_network_ip` hashes each name across .2..broadcast-1) + # and dnsmasq on the bridge is DNS-only. hive-ci is the one service + # container on the bridge, so it needs a static address too — `useDHCP` + # here only hangs the boot waiting for a lease nothing serves. Reserve the + # top host address of the (default /24) subnet; a clash with an agent that + # happens to hash here is the same rename-to-resolve case as any + # agent/agent IP collision. Operators on a non-/24 bridge (or with + # `bridgeIp` set to the top address) should pick a free host address. + ciBridgeOctets = lib.splitString "." networkCfg.bridgeIp; + ciBridgeIp = "${lib.elemAt ciBridgeOctets 0}.${lib.elemAt ciBridgeOctets 1}.${lib.elemAt ciBridgeOctets 2}.254"; + # Self-signed TLS is the gateway default (no operator cert / ACME). When # active, forgejo's ROOT_URL is `https://forge.` and the leaf is # signed by the host hive CA — so the runner's Node-based actions (e.g. @@ -410,14 +423,21 @@ in # affect traffic destined for the bridge IP itself. networking.nameservers = [ networkCfg.bridgeIp ]; # With privateNetwork=true + hostBridge the container's veth - # is bridge-attached. Enable DHCP on eth0 so the container gets - # an IP from dnsmasq on the bridge (hive-gateway serves the - # bridge subnet). Per-interface rather than global: nixos-containers - # sets networking.useDHCP = false for all containers (to silence - # the deprecated global DHCP warning), so setting useDHCP = true - # globally would conflict. eth0 is the inner veth name assigned - # by systemd-nspawn when hostBridge is used. - networking.interfaces.eth0.useDHCP = true; + # (eth0) is bridge-attached. There is no DHCP server on the hive + # bridge (dnsmasq is DNS-only; agents use static IPs), so assign a + # static address + default route via the bridge gateway rather than + # DHCP — `useDHCP` here just hangs boot on a lease that never + # arrives. See `ciBridgeIp` above. + networking.interfaces.eth0.ipv4.addresses = [ + { + address = ciBridgeIp; + prefixLength = networkCfg.bridgePrefixLength; + } + ]; + networking.defaultGateway = { + address = networkCfg.bridgeIp; + interface = "eth0"; + }; # nspawn containers can't create user-namespaces, so nix # sandboxing always fails. Fall back to unsandboxed builds.