Files
register-referentie/docs/runbooks/gitea-actions-gotchas.md
Niek Otten 9ff7937055
Some checks failed
CI / lint (pull_request) Successful in 50s
CI / build (pull_request) Successful in 40s
CI / unit (pull_request) Successful in 45s
CI / compose-smoke (pull_request) Failing after 3m31s
fix(infra): bake config into images so compose-smoke passes on CI (refs #30)
Root cause of the compose-smoke failure (found in the runner logs):

  oz-init-1 | CommandError: Yaml file
              `/app/setup_configuration/data.yaml` does not exist.

The ubuntu-latest runner runs the job inside a container, so
`docker compose up` starts the stack as SIBLING containers via the host
daemon. A relative bind mount (./openzaak/setup_configuration) resolves to
a path inside the job container that the daemon can't see, so Docker mounts
an empty dir and the init container can't find data.yaml. The same trap hit
nrc-init (data.yaml), flowable-init (the BPMN) and keycloak (realm import).

Fix: bake the assets into small derived images instead of bind-mounting:
  - infra/openzaak/Dockerfile        -> register-referentie/openzaak:dev
  - infra/opennotificaties/Dockerfile-> register-referentie/opennotificaties:dev
  - infra/keycloak/Dockerfile        -> register-referentie/keycloak:dev
  - flowable-init: build.dockerfile_inline bakes workflows/registratie.bpmn

Base versions stay build args (OPENZAAK_TAG / OPENNOTIFICATIES_TAG), so the
pinning is unchanged. Applied to both the consolidated compose and the
per-service composes, so local Podman and CI use one mechanism — no bind
mounts, no SELinux `:z`, no world-readable requirement.

Verified locally: `podman build` of the OpenZaak and BPMN images produces
the file at the expected in-container path.

Docs: docs/runbooks/gitea-actions-gotchas.md explains the DinD bind-mount
trap and the bake fix; openzaak.md and ci.md point at it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:06:56 +02:00

4.1 KiB

Gitea Actions gotchas

Known differences between Gitea Actions (our CI) and a plain local run, and the workarounds we adopted. Referenced by CLAUDE.md §8.7 and §15.

Bind mounts don't reach Compose services on the hosted runner

Symptom. make smoke is green locally but the compose-smoke CI job fails with the OpenZaak init container exiting 1:

oz-init-1 | CommandError: Yaml file `/app/setup_configuration/data.yaml` does not exist.

Migrations run fine; only the step that reads a mounted file fails. The same class of failure hits any service that bind-mounts a workspace path — nrc-init (its data.yaml), flowable-init (the BPMN), keycloak (the realm import dir).

Cause. The ubuntu-latest runner executes the whole job inside a container (docker.gitea.com/runner-images:ubuntu-latest). When the job then runs docker compose ... up, Compose talks to the host's Docker daemon and starts the stack as sibling containers. A relative bind mount such as

volumes:
  - ./openzaak/setup_configuration:/app/setup_configuration:ro

is resolved by Compose to an absolute path inside the job container (/workspace/eho/register-referentie/infra/openzaak/setup_configuration). The daemon then looks for that path on its own host, doesn't find it, and auto-creates an empty directory to mount. The container starts with an empty mount point, so the file appears "missing".

This is the classic Docker-in-Docker / sibling-container bind-mount trap. It does not happen on a runner that executes jobs directly on the host (the previous self-hosted respellion-linux setup), which is why switching to ubuntu-latest exposed it.

Fix: bake assets into derived images instead of bind-mounting them. Anything a Compose service needs at runtime that lives in the repo is COPY-ed into a small derived image, so it is present regardless of where the daemon runs:

Asset Derived image Dockerfile
OpenZaak setup_configuration/data.yaml register-referentie/openzaak:dev infra/openzaak/Dockerfile
Open Notificaties setup_configuration/data.yaml register-referentie/opennotificaties:dev infra/opennotificaties/Dockerfile
Keycloak realm exports register-referentie/keycloak:dev infra/keycloak/Dockerfile
workflows/registratie.bpmn register-referentie/flowable-init:dev build.dockerfile_inline in the compose files

The base image tag stays a build arg (OPENZAAK_TAG, OPENNOTIFICATIES_TAG) so version pinning is unchanged. The three *-init/web/celery services that share a base now share one built image tag, so the bake happens once per up --build.

Why not the alternatives.

  • Compose configs: with inline content — Compose materialises these as a temp file on the client side and bind-mounts it, so it hits the exact same daemon-can't-see-the-path problem.
  • A self-hosted runner that runs jobs on the host — works, but reintroduces a bespoke runner and undoes the move to the hosted ubuntu-latest label.

Consequence for local dev. There is now no bind mount of these config files, so the SELinux :z/:Z relabel flag is no longer needed anywhere in infra/, and rootless Podman no longer needs the files to be world-readable. One mechanism (build) works on both Podman locally and Docker-in-Docker in CI.

--wait needs an explicit timeout

docker compose up --wait defaults to a 60-second timeout in some Compose v2 releases. A cold OpenZaak migrate alone takes ~50 s, so the smoke target passes --wait-timeout 300 (see Makefile). The 3-minute Definition-of-Done budget still holds — this just stops --wait giving up before the stack is healthy.

PostGIS readiness vs. pg_isready

pg_isready reports the server is accepting connections as soon as the TCP port is open — before the postgis/postgis image has finished running its CREATE EXTENSION postgis init scripts. An init container that starts migrating in that window can fail on a missing PostGIS. The db healthchecks therefore add a SELECT PostGIS_Version() probe so dependents wait for the extension, not just the port.