1 05 Forgejo Actions Runner as a bootc Image
Eric the IT Guy edited this page 2026-08-12 12:49:28 -05:00

Part 5: A Forgejo Actions Runner as a bootc Image

The runner (itg-prd-run) is the thing that actually builds my images, and it is itself a bootc image. Very meta, and it means the CI executor is as reproducible as everything else.

Building the runner image

FROM itg-base, then:

  • Pull the forgejo-runner binary at build time (an ARG RUNNER_VERSION and a curl into /usr/bin/forgejo-runner).
  • dnf install git nodejs, because Actions needs node for most actions and git for checkout.
  • A config.yaml, a systemd service to run the runner, and a tmpfiles.d entry for /var/lib/forgejo-runner.
  • A dedicated data disk for /var/lib/containers via a .mount and a first-boot format service, so all the image-build layers land on their own disk and do not fill the root.

The registration that actually works

This is the part that cost me hours. The old, widely-documented forgejo-runner register command does not work against a current Forgejo server. Every fresh token came back "token not found," and the runner logged "0 server connections configured, terminating."

The working path on modern Forgejo is the config-based server.connections block, and the missing piece was a uuid. In the Forgejo UI you use "Create new runner," which creates the runner record and gives you a uuid alongside the token. You put all three into the runner's config:

server:
  connections:
    forge:
      url: https://forge.itguyeric.com/
      uuid: <uuid-from-the-Create-new-runner-UI>
      token: <token>
      labels:
        - native:host

The uuid is what ties the running daemon to the runner record the server already created. Without it, the server has no idea who is calling.

Where the config lives

That config sits in /etc, and I edit it live on the box rather than baking it into the image, because it carries a per-instance secret (the token). This is a deliberate seam: /etc is 3-way merged, so my live registration survives every bootc upgrade. Secrets stay machine-local; the image stays clean.

The Proxmox boot gotcha

The runner VM would not boot: "No bootable option." The SCSI controller was set to the default LSI 53C895A, and OVMF (UEFI firmware) has no driver for it, so it could not see the disk. Switching the controller to VirtIO SCSI fixed it instantly. This applies to every VM in this series, LSI plus UEFI equals a black screen.

Lessons

  • Register modern Forgejo runners with server.connections (url + uuid + token), not the deprecated register command. The uuid is mandatory and comes from "Create new runner."
  • Keep the runner's token in /etc and let the merge preserve it across upgrades, rather than baking a secret into the image.
  • Use VirtIO SCSI, not LSI, on any UEFI Proxmox VM.