Files
trx-rs/container
sjg ff4e2a5c5d
CI / lint (pull_request) Successful in 9m35s
CI / reuse (pull_request) Failing after 5s
CI / lint (push) Successful in 8m40s
CI / test (pull_request) Successful in 29m0s
CI / reuse (push) Failing after 5s
CI / test (push) Successful in 14m28s
[chore](trx-rs): install reuse with charset-normalizer extra
The runner image's `reuse` failed to import (NoEncodingModuleError): it
needs an encoding-detection backend, which the bare `reuse` install does
not provide and which libmagic/`file` is not present to satisfy. Install
`reuse[charset-normalizer]` so the reuse job can run in the image.

Rebuild the runner image and recreate the containers to pick this up.

Assisted-By: Claude Code (claude-opus-4)
Claude-Session: https://claude.ai/code/session_01NFpGtGTWUEYXLwZeZs2RAV
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-07-18 23:57:38 +02:00
..

Podman-based Gitea Actions runners

Run two independent Gitea Actions runners on one host as rootless Podman containers managed by systemd (Quadlet) — one per project — instead of two VMs. Uses the host executor: workflow steps run directly inside a purpose-built runner image that already has the Rust toolchain and all build dependencies baked in, so CI runs skip the per-run install cost and no Docker/Podman socket is needed.

Files

File Purpose
Containerfile Runner image: Debian + build deps + clang + Rust + Node + reuse + act_runner.
entrypoint.sh Registers on first boot (if needed), then runs the daemon.
config.yaml act_runner config template (seeded into each runner's volume).
trx-rs-runner.container Quadlet unit for the trx-rs runner.
project2-runner.container Quadlet unit for the second project's runner.

Prerequisites (once per host)

Rootless Podman with cgroups v2 (default on modern distros). As the unprivileged user that will own the runners:

# Survive logout / start on boot without an interactive session.
loginctl enable-linger "$USER"

No podman.socket is required for the host executor.

1. Build the image

cd container
podman build -t trx-rs-ci:latest .

2. Get a registration token

For each repo: Settings → Actions → Runners → Create new Runner and copy the token. (Org- or instance-level tokens work too if you prefer wider scope.)

3. Install and start the runners

mkdir -p ~/.config/containers/systemd
cp trx-rs-runner.container project2-runner.container ~/.config/containers/systemd/

# Paste each repo's token for the FIRST boot only:
#   Environment=GITEA_RUNNER_REGISTRATION_TOKEN=xxxx…
$EDITOR ~/.config/containers/systemd/trx-rs-runner.container
$EDITOR ~/.config/containers/systemd/project2-runner.container

systemctl --user daemon-reload
systemctl --user start trx-rs-runner
systemctl --user start project2-runner

systemctl --user status trx-rs-runner
podman logs -f gitea-runner-trx-rs

Once each runner shows online in the repo's runner list, blank out the GITEA_RUNNER_REGISTRATION_TOKEN line again (the registration is persisted in the …-data volume) and systemctl --user daemon-reload.

Required workflow change: the reuse job

The host executor runs steps directly in the container and therefore cannot run Docker-based actions. The current reuse job uses fsfe/reuse-action@v5, which is a Docker action. reuse is baked into the image, so replace that job with a plain command:

  reuse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: REUSE compliance
        run: reuse lint

The lint and test jobs need no changes: their sudo apt-get … and rustup steps still run, but become fast no-ops because the image already has those packages and the toolchain. (sudo is included in the image for exactly this reason.)

If you would rather keep Docker-based actions and per-run images, use the Docker executor instead: drop the :host suffix from the label in config.yaml, enable systemctl --user --now enable podman.socket, mount it into the container, and set container.docker_host to the socket path. That trades the baked-in speed for stronger per-job isolation.

Tuning

  • capacity (in config.yaml) — concurrent jobs per runner. Rust builds are heavy; 12 is sensible when two runners share a host.
  • PodmanArgs=--cpus/--memory (in each .container) — hard resource caps so one project cannot starve the other.
  • SELinux — the :Z volume flag is already set; keep it if SELinux is enforcing.

Committing these files

If you add this directory to a REUSE-checked repo, register the markdown in REUSE.toml (the other files carry inline SPDX headers):

[[annotations]]
path = ["container/**"]
SPDX-FileCopyrightText = "2026 Stan Grams <sjg@haxx.space>"
SPDX-License-Identifier = "GPL-2.0-or-later"