furtka/docs/runner-setup.md
Daniel Maksymilian Syrnicki 6cf65f2c36
Some checks failed
CI / lint (push) Successful in 1m12s
CI / test (push) Successful in 32s
CI / validate-json (push) Successful in 22s
CI / markdown-links (push) Failing after 2s
ci: stand up forge-runner-01 with DinD sidecar, fix doc label bug
Bootstrap script + compose + config checked in under ops/forgejo-runner/
so a second runner is a scripted setup. runner-setup.md corrects the
register label format (<name>:docker://<image>, not bare names) and
documents the Ubuntu systemd-resolved DNS gotcha.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 21:31:35 +02:00

4.6 KiB

Forgejo Runner Setup

How to stand up a forgejo-runner so the CI workflow in .forgejo/workflows/ci.yml actually executes on every push.

The runner is a long-running daemon that polls the Forgejo instance for queued jobs and runs them in Docker containers.

A ready-to-use bootstrap script and compose file live under ops/forgejo-runner/.

Choosing a host

Option Good for Trade-off
Dedicated VPS Production-ish CI that runs even when you're offline Costs a few €/month; one more machine to maintain
Home server / NAS Free; plenty of capacity CI blocked if home network / power drops
Local dev machine Quick to set up, fast runs CI only works while the machine is on

Recommendation for now: home server or a cheap VPS. Don't use a laptop that suspends.

Install

Pick either the binary or the Docker container path. Docker is easier to upgrade.

Copy ops/forgejo-runner/compose.yml and ops/forgejo-runner/config.yml from this repo to the host, e.g. into ~/forgejo-runner/ (compose file) and ~/forgejo-runner/data/ (config file). The runner talks to a sidecar Docker-in-Docker container via tcp://docker-in-docker:2375, so the host's own Docker socket is not exposed to jobs.

If the host is a fresh Ubuntu VM, run ops/forgejo-runner/bootstrap.sh first to install Docker Engine + the Compose plugin from the official repo.

Path B: Binary

Download the latest release from https://code.forgejo.org/forgejo/runner/releases and drop it somewhere in $PATH:

wget https://code.forgejo.org/forgejo/runner/releases/download/v6.0.0/forgejo-runner-6.0.0-linux-amd64
chmod +x forgejo-runner-6.0.0-linux-amd64
sudo mv forgejo-runner-6.0.0-linux-amd64 /usr/local/bin/forgejo-runner

Register

  1. In the Forgejo web UI: go to Site Administration → Actions → Runners → Create new Runner. Copy the registration token. (For a repo-scoped runner instead, use Repo Settings → Actions → Runners.)

  2. Register from the runner host by running the registration inside a one-shot container so the output lands in the mounted data/ directory:

    cd ~/forgejo-runner
    docker run --rm -v "$PWD/data:/data" code.forgejo.org/forgejo/runner:6 \
      forgejo-runner register \
        --instance https://forgejo.sourcegate.online \
        --token <TOKEN> \
        --name forge-runner-01 \
        --labels 'docker:docker://catthehacker/ubuntu:act-latest,ubuntu-latest:docker://catthehacker/ubuntu:act-latest,self-hosted:docker://catthehacker/ubuntu:act-latest' \
        --no-interactive
    

    Labels must use the <name>:docker://<image> form — bare labels (ubuntu-latest) get stored as ubuntu-latest:host, which tells the runner to execute jobs directly inside the runner container (no Python, no git, nothing). catthehacker/ubuntu:act-latest is the common drop-in image with GitHub Actions tooling preinstalled.

  3. Start the daemon: docker compose up -d.

  4. Verify the runner shows up as Idle in Forgejo's admin Runners page and the log prints runner: forge-runner-01, ..., declared successfully.

First CI run

Push any commit; the Actions tab on the repo should show the workflow running. If nothing happens:

  • Confirm the runner is online (Forgejo admin → Actions → Runners).
  • Check the workflow has labels that match the runner (runs-on: ubuntu-latest needs a runner registered with that label).
  • Check the runner logs: docker logs forgejo-runner or the systemd journal.

Systemd unit (for the binary path)

[Unit]
Description=Forgejo Actions Runner
After=docker.service
Requires=docker.service

[Service]
ExecStart=/usr/local/bin/forgejo-runner daemon
WorkingDirectory=/var/lib/forgejo-runner
User=forgejo-runner
Restart=on-failure

[Install]
WantedBy=multi-user.target

Save as /etc/systemd/system/forgejo-runner.service, then sudo systemctl enable --now forgejo-runner.

Security notes

  • Jobs run inside a Docker-in-Docker sidecar, not against the host's Docker socket. Still, DinD runs privileged — give the runner its own VM, not a shared host.
  • Registration tokens are one-shot; a stolen token can't re-register after the runner is live.
  • Prefer repo-scoped runners over instance-wide if you're sharing the runner with other repos you don't control.
  • Ubuntu's default systemd-resolved makes the host's stub resolver (127.0.0.53) inherit a LAN DNS server that Docker containers may not be able to reach. If container DNS fails, set explicit upstream DNS in /etc/docker/daemon.json (e.g. {"dns": ["1.1.1.1", "8.8.8.8"]}) and sudo systemctl restart docker.