From 2a6f14129a655c7e498739ecd2fb549ecd7459b6 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sun, 19 Apr 2026 12:24:41 -0500 Subject: [PATCH] ci: build against a dind sidecar instead of the host socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The host docker socket wasn't mounted consistently across act_runners, so which runner picked up the push was the difference between a clean build and "unable to connect to docker." Switch to a dockerd service pinned to the same bridge network as the job; the CLI now talks to it over TCP via DOCKER_HOST=tcp://dockerd:2375. Every runner has a daemon, every time — no more re-runs for the scheduling lottery. --- .forgejo/workflows/build.yaml | 42 ++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 6a1d2391..9258445f 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -18,6 +18,22 @@ jobs: name: Build and Push Docker Image runs-on: ubuntu-22.04 + # Runs a dockerd sidecar on the same bridge network as the job + # container so the build doesn't depend on the host's docker socket + # being mounted. Some act_runners don't mount it and there was no + # way to tell at schedule time which runner would win — leading to + # random "unable to connect to docker" failures that went away on a + # re-run. With dind every runner has a daemon, every time. + services: + dockerd: + image: docker:27-dind + options: --privileged + env: + DOCKER_TLS_CERTDIR: "" + + env: + DOCKER_HOST: tcp://dockerd:2375 + steps: - name: Checkout code uses: https://github.com/actions/checkout@v4 @@ -26,8 +42,8 @@ jobs: run: | # Dodge apt entirely — the runner image's bookworm repos have been # failing GPG verification with "At least one invalid signature". - # The static Docker client binary talks to the host's daemon socket - # that act_runner mounts into the job container. buildx is needed + # The static Docker client binary talks to the dind sidecar via + # the DOCKER_HOST env var set at the job level. buildx is needed # separately because the Dockerfile uses `--mount=type=cache`, # which is a BuildKit-only feature. curl -fsSL -o /tmp/docker.tgz \ @@ -44,28 +60,22 @@ jobs: docker --version docker buildx version - - name: Wait for Docker daemon + - name: Wait for dockerd sidecar run: | - # act_runner mounts the host docker socket into the job, but - # the daemon side sometimes takes a moment to answer. Poll - # `docker info` until it responds so the login/build steps - # below don't race the socket and fail with "unable to - # connect to docker". - sock="${DOCKER_HOST:-unix:///var/run/docker.sock}" - echo "Using DOCKER_HOST=${sock}" + # dind takes ~5-15s to finish booting inside the service + # container; poll until the TCP endpoint answers. + echo "Using DOCKER_HOST=${DOCKER_HOST}" - for i in $(seq 1 30); do + for i in $(seq 1 60); do if docker info >/dev/null 2>&1; then - echo "Docker daemon reachable after ${i}s" + echo "dockerd reachable after ${i}s" exit 0 fi - echo "Waiting for Docker daemon... ($i/30)" + echo "Waiting for dockerd... ($i/60)" sleep 1 done - echo "Docker daemon never became reachable — diagnostics:" >&2 - echo "--- ls -l /var/run/docker.sock ---" >&2 - ls -l /var/run/docker.sock 2>&1 || true + echo "dockerd never became reachable — diagnostics:" >&2 echo "--- env | grep -i docker ---" >&2 env | grep -i docker 2>&1 || true echo "--- docker info (verbose) ---" >&2