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.
This commit is contained in:
sock 2026-07-10 17:30:25 +02:00 committed by mara
commit 078ea74ba9

View file

@ -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.<domain>` 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.