chore(infra): pin act_runner image pull policy
CI / commits (pull_request) Successful in 1m22s
CI / check (pull_request) Successful in 1m29s
CI / scan (pull_request) Failing after 1m32s
CI / a11y (pull_request) Successful in 1m35s
CI / perf (pull_request) Successful in 2m55s

`act_runner`'s default `container.force_pull: true` re-issues a
`docker pull` at the start of every job, adding 10-30 s of registry
round-trip per job even when every layer is already locally cached.
The job images are already pinned to specific tags (catthehacker
ubuntu :act-22.04 + :full-22.04), so the implicit pull is both pure
overhead and contradictory with the deliberate-upgrade policy
ADR-0015 spells out for the runner image itself.

Mount a shared `infra/runner-config.yaml` into all three runners
with `force_pull: false`, point each runner at it via the
`CONFIG_FILE` env var, and document the pre-pull procedure (one-shot
`docker pull` on the runner host, plus the upgrade playbook) in
infra/README.md "Job image pinning and pre-pull".

The pre-pull is also folded into the "First-time registration"
walkthrough so a fresh setup is correct end-to-end.
This commit is contained in:
Julien Gautier
2026-05-04 15:45:18 +02:00
parent 7951908229
commit 4934fc29da
3 changed files with 65 additions and 3 deletions
+36 -3
View File
@@ -5,6 +5,7 @@ Infrastructure-as-code artefacts for the project. Separate from application code
| Subject | File / Folder | ADR / Reference |
| -------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------- |
| Self-hosted CI runners (Gitea Actions) | [`ci-runners.compose.yml`](ci-runners.compose.yml) | [ADR-0015 §"Runners"](../docs/decisions/0015-cicd-gitea-actions.md) |
| Shared `act_runner` configuration | [`runner-config.yaml`](runner-config.yaml) | [ADR-0015 §"Runners"](../docs/decisions/0015-cicd-gitea-actions.md) |
| Runtime state of the runners | `data/` (git-ignored after `.gitignore`) | — |
| Env-vars template for the runners | `.env.example` (`.env` is git-ignored) | — |
@@ -35,10 +36,15 @@ $EDITOR .env
# Set GITEA_INSTANCE_URL (https, no trailing slash) and
# GITEA_RUNNER_REGISTRATION_TOKEN.
# 3. Bring the runners up.
# 3. Pre-pull the job images so the runner doesn't have to (see
# "Job image pinning and pre-pull" below for the rationale).
docker pull catthehacker/ubuntu:act-22.04
docker pull catthehacker/ubuntu:full-22.04
# 4. Bring the runners up.
docker compose -f ci-runners.compose.yml up -d
# 4. Verify in Gitea: the three runners appear as online with the
# 5. Verify in Gitea: the three runners appear as online with the
# self-hosted, on-prem labels. If a runner doesn't come online,
# inspect its logs:
docker compose -f ci-runners.compose.yml logs runner-1
@@ -75,7 +81,7 @@ When this is reactivated, the right fix is one of:
Either path needs a single-runner spike before being rolled out to the three.
### Image pinning
### `act_runner` image pinning
The compose pins `gitea/act_runner:0.2.13`. Update the pin deliberately, not via `:latest`:
@@ -86,6 +92,33 @@ The compose pins `gitea/act_runner:0.2.13`. Update the pin deliberately, not via
The matching CI workflows refer to runner _labels_ (not images), so a runner-image upgrade does not affect `.gitea/workflows/*`.
### Job image pinning and pre-pull
`act_runner` runs each job inside a container whose image is selected by the runner's _labels_. Two images are in use:
| Label | Image | Used by |
| ---------------------- | -------------------------------- | ---------------------------------- |
| `self-hosted` | `catthehacker/ubuntu:act-22.04` | `check`, `scan`, `commits`, `a11y` |
| `on-prem` | `catthehacker/ubuntu:act-22.04` | (alias of `self-hosted`) |
| (per-job `container:`) | `catthehacker/ubuntu:full-22.04` | `perf` (Lighthouse needs Chrome) |
[`runner-config.yaml`](runner-config.yaml) sets `container.force_pull: false`. Without that, act_runner re-issues a `docker pull` at the start of every single job (~1030 s of registry round-trip even when every layer is already cached), which both wastes wall-clock and contradicts our policy of upgrading job images deliberately rather than implicitly via `:latest`.
The trade-off: the host Docker daemon must already hold the images locally. Pre-pull them once after a fresh runner host install:
```bash
docker pull catthehacker/ubuntu:act-22.04
docker pull catthehacker/ubuntu:full-22.04
```
Upgrading to a newer tag is a deliberate three-step process:
1. Edit `GITEA_RUNNER_LABELS` (in [`ci-runners.compose.yml`](ci-runners.compose.yml)) and / or the per-job `container.image:` (in `.gitea/workflows/*`) to the new tag.
2. On the runner host, `docker pull <new-tag>` so the image is locally available before the next CI job starts.
3. Commit on a feature branch with a `chore(deps):` Conventional Commits subject; one of `chore(deps): upgrade CI job image to ...`.
Old, no-longer-referenced images can be reaped during the periodic `docker system prune -af` (see "Disk pressure" above).
---
## Future infra concerns — placeholders