Forgejo runner setup on Fedora

Topics: Forgejo, Fedora Workstation, Podman

This setup runs Forgejo CI jobs in Podman containers through Podman’s Docker-compatible socket.

Summary

Forgejo Runner can run CI jobs in Podman containers by using Podman’s Docker-compatible socket. The runner labels still use the docker:// label type, even when the backend is Podman.

Runner labels

Use docker labels for Podman-backed jobs:

runner:
  labels:
    - docker:docker://ghcr.io/catthehacker/ubuntu:act-22.04
    - ubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:act-22.04

Workflows can then use either:

runs-on: docker

or, for GitHub-style compatibility:

runs-on: ubuntu-latest

ubuntu-latest is only the Forgejo runner label. The actual container image is whatever the runner config maps it to.

Enable the rootless Podman socket

Podman does not run a daemon by default. Forgejo Runner needs the Docker-compatible Podman socket.

Configure subuid and subgid mappings

Rootless Podman also needs subordinate UID and GID ranges for the runner user. This is easy to miss when the runner account was created as a system user.

Check whether mappings already exist:

grep '^forgejo-runner:' /etc/subuid /etc/subgid

If there is no entry, assign a range:

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 forgejo-runner

Use a range that does not overlap with another user’s existing /etc/subuid or /etc/subgid range. The example above gives the runner 65,536 subordinate IDs, which is the usual rootless Podman size.

If Podman had already been used by that account before the mapping was added, migrate its rootless storage metadata:

uid=$(id -u forgejo-runner)
sudo -u forgejo-runner \
  XDG_RUNTIME_DIR="/run/user/$uid" \
  DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$uid/bus" \
  podman system migrate

Verify the mapping:

uid=$(id -u forgejo-runner)
sudo -u forgejo-runner \
  XDG_RUNTIME_DIR="/run/user/$uid" \
  podman unshare cat /proc/self/uid_map

For a dedicated runner user, first enable lingering and start the user’s systemd scope:

sudo loginctl enable-linger forgejo-runner
uid=$(id -u forgejo-runner)
sudo systemctl start "user@$uid.service"

Then enable the Podman socket in that user’s systemd scope:

sudo -u forgejo-runner \
  XDG_RUNTIME_DIR="/run/user/$uid" \
  DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$uid/bus" \
  systemctl --user enable --now podman.socket

Verify the socket exists:

ls -l "/run/user/$uid/podman/podman.sock"

Configure Forgejo Runner

Point the runner at the rootless Podman socket:

container:
  docker_host: unix:///run/user/<runner-uid>/podman/podman.sock

Replace <runner-uid> with the UID of the runner user.

Common failure

Running systemctl --user via sudo -iu forgejo-runner can fail with:

Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined

That means the user systemd bus and runtime directory were not available in the sudo session. Start the user’s systemd service and set XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS explicitly, as shown above.

Rootful fallback

Rootful Podman also works, but gives CI jobs a much larger crowbar:

sudo systemctl enable --now podman.socket
ls -l /run/podman/podman.sock

Runner config:

container:
  docker_host: unix:///run/podman/podman.sock

Prefer rootless unless there is a specific reason to let CI jobs talk to a rootful container backend.