The Docker-based runner's own container is too minimal for host-mode jobs (no rsync, docker CLI, or Node.js) and has no reliable path to the host filesystem or systemctl otherwise. Register a separate, native runner (label deploy:host) just for deployment, and point the deploy job at it.
3.4 KiB
Deployment
MapTool deploys as a Docker container on the same server the Gitea Actions runners run on. Two separate runners are registered against the Gitea instance:
-
The existing Docker-based runner (
gitea/runner:3), labelsubuntu-latest/ubuntu-24.04/ubuntu-22.04— runs thecheckjob in a fresh, isolated container per job, same as before. -
A dedicated native runner, installed directly on the host (no Docker around it), label
deploy:host— runs only thedeployjob.
They’re separate because the Docker-based runner’s jobs (whether in
its usual isolated per-job containers, or tried directly inside its
own container via a host-scheme label) have no reliable access to
the host’s filesystem, systemctl, or even a full toolset (its own
container image is missing rsync, docker CLI, Node.js — it’s built
for spawning per-job containers via its docker.sock mount, not for
running arbitrary host commands). The native runner sidesteps all of
that: it is a host process, so docker, git, etc. are just
whatever’s installed on the server.
Layout on the server
/srv/docker/maptool/ holds:
-
A checkout of this repository (kept in sync by the deploy job, see below) —
Dockerfile,docker-compose.yaml, application code. -
data/, not part of that sync — uploaded GPX files andoffline.pmtiles. Persisted via a bind mount (./data:/app/dataindocker-compose.yaml), so a redeploy (docker compose up --build) never touches it.
The native deploy runner
Installed from the official binary (see
https://docs.gitea.com/runner/installation/binary/), running as a
systemd service (gitea-runner.service) under a dedicated
gitea-runner system user, which needs:
-
Docker access (member of the
dockergroup). -
Write access to
/srv/docker/maptool.
Its config.yaml (/etc/gitea-runner/config.yaml) declares a single
label:
runner:
labels:
- "deploy:host"
The host scheme means jobs run directly via the runner process’s own
shell — no container spawned — which is exactly what a plain, single
purpose deploy runner needs.
Puma’s bind address
config/puma.rb binds to 127.0.0.1 by default (see
docs/architecture.adoc) — fine for local dev, but inside a container
that would make the app unreachable through Docker’s published port
(loopback is scoped to the container’s own network namespace).
docker-compose.yaml sets BIND_HOST=0.0.0.0 to override that for
the deployed container specifically; Docker’s own port publishing
(ports: in the compose file) is the network boundary there instead
of Puma’s bind address.
CI deploy job
.gitea/workflows/ci.yml’s `deploy job runs after check passes, only
on a push to main, with runs-on: deploy:
-
Checks out the repo with plain
git(fetch+checkout, authenticated via a one-offhttp.extraHeader) rather thanactions/checkout@v4— a leftover from when this job ran in a Node-less container; kept as-is since there’s no reason to bring Node back in for something this already does. -
Copies the checkout into
/srv/docker/maptool/using plaincp/rm: clears everything in the target exceptdata/, copies the fresh checkout over, then drops the copied.git. -
cd /srv/docker/maptool && docker compose -p maptool up --build -d— rebuilds the image and recreates the container if anything changed; a no-op if nothing did.