From 9f21d349f6dd7a7d565c29a89b1c90101495bb95 Mon Sep 17 00:00:00 2001 From: NamPham Date: Sun, 6 Sep 2026 08:28:46 +0700 Subject: [PATCH] dind: split DNS -- ts.net via MagicDNS, everything else via Cloudflare The host resolves the tailnet through MagicDNS on tailscale0 and everything else through Cloudflare (systemd-resolved split DNS). MagicDNS has no upstream for names outside the tailnet and answers them SERVFAIL, so handing 100.100.100.100 to job containers as their first resolver made every public lookup wait on a failure before falling back: image pulls stalled and the e2e lane's *.localtest.me fetches blew a 5s poll. A CoreDNS forwarder at a fixed address on the runner network now answers both: ts.net is forwarded to MagicDNS, the rest to 1.1.1.1/1.0.0.1. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_018j9YG8YFjcsVLnqzNz7LmA --- Corefile | 19 +++++++++++++++++++ docker-compose.yml | 22 ++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 Corefile diff --git a/Corefile b/Corefile new file mode 100644 index 0000000..c5e10f0 --- /dev/null +++ b/Corefile @@ -0,0 +1,19 @@ +# Split-horizon resolver for the runner's Docker daemon and every job +# container it starts. +# +# The host resolves the tailnet through MagicDNS on tailscale0 and everything +# else through Cloudflare on the default link (systemd-resolved split DNS). +# MagicDNS itself has no upstream configured for names outside the tailnet and +# answers them with SERVFAIL, so handing 100.100.100.100 to job containers as +# their first resolver made every public lookup (image pulls, *.localtest.me +# in the e2e lanes) wait on a failure before falling back -- long enough to +# time out a 5s Playwright poll. Only ts.net goes to MagicDNS here. +ts.net:53 { + errors + forward . 100.100.100.100 +} +.:53 { + errors + cache 30 + forward . 1.1.1.1 1.0.0.1 +} diff --git a/docker-compose.yml b/docker-compose.yml index 854373d..fda8e8e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,17 @@ services: + # Split DNS for the dind daemon and its job containers: ts.net -> MagicDNS, + # everything else -> Cloudflare. See Corefile for why MagicDNS alone fails. + dns: + image: coredns/coredns@sha256:af8c8d35a5d184b386c4a6d1a012c8b218d40d1376474c7d071bb6c07201f47d + container_name: gitea-runner-dns + restart: unless-stopped + command: ["-conf", "/Corefile"] + volumes: + - ./Corefile:/Corefile:ro + networks: + runner: + ipv4_address: 172.28.0.53 + # Isolated Docker daemon for the runner's job containers. # Keeps the host Docker clean and gives reliable cache networking. dind: @@ -37,12 +50,14 @@ services: # own network, and a killed job leaves one behind. 10.210/16 in /24s is 256 # networks and collides with nothing here (no 10.x route on the host; the # tailnet is 100.64/10). - command: ["dockerd", "--host=tcp://0.0.0.0:2375", "--host=unix:///var/run/docker.sock", "--host=unix:///dind-sock/docker.sock", "--tls=false", "--dns=100.100.100.100", "--dns=1.1.1.1", "--default-address-pool", "base=10.210.0.0/16,size=24"] + command: ["dockerd", "--host=tcp://0.0.0.0:2375", "--host=unix:///var/run/docker.sock", "--host=unix:///dind-sock/docker.sock", "--tls=false", "--dns=172.28.0.53", "--dns=1.1.1.1", "--default-address-pool", "base=10.210.0.0/16,size=24"] environment: DOCKER_TLS_CERTDIR: "" dns: + - 172.28.0.53 - 1.1.1.1 - - 1.0.0.1 + depends_on: + - dns networks: - runner volumes: @@ -92,6 +107,9 @@ services: networks: runner: + ipam: + config: + - subnet: 172.28.0.0/24 volumes: dind-storage: