Forgejo runner Ansible role
- Jinja 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| defaults | ||
| meta | ||
| tasks | ||
| templates | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| README.md | ||
Forgejo Runner
Deploys Forgejo Actions runners that start from scratch for every job. Each runner has its own Docker-in-Docker daemon, runs exactly one job (forgejo-runner one-job --wait) and is then thrown away together with everything the job left behind: containers, images, build cache, volumes.
How it works
- One systemd unit per runner,
forgejo-runner@<name>. Each start runsdocker compose upfor two containers:dind(docker:dind, privileged) andrunner. - The runner waits for a job, runs it inside its own Docker daemon and exits. Compose then stops the daemon,
docker compose down --volumesdeletes its storage, and systemd starts the next round. - Jobs can use Docker (
docker build,docker compose, …) throughDOCKER_HOST=tcp://dind:2375. Job containers themselves are unprivileged, get a new network per job and can't mount host paths. - Runners don't share anything: separate Docker daemons,
capacity: 1. - The runner config (with the runner secret) is written to
forgejo_runner_secret_path(/run, a tmpfs) only. After a reboot it is gone and the units are skipped until this role runs again. - Nothing is cached between jobs, so every job pulls its images again.
Requirements
- Docker with the Compose plugin
- Each runner registered on the Forgejo side with the same secret, e.g. via the
forgejorole'sforgejo_runners
Variables
| Variable | Default | Description |
|---|---|---|
forgejo_runner_url |
~ |
Required. Forgejo URL with trailing slash. |
forgejo_runner_instances |
[] |
Runners on this host. See below. |
forgejo_runner_labels |
ubuntu-latest, docker |
<label>:docker://<image> entries offered to runs-on. |
forgejo_runner_version |
13 |
data.forgejo.org/forgejo/runner image tag. |
forgejo_runner_dind_version |
28-dind |
docker image tag of the Docker-in-Docker daemon. |
forgejo_runner_extra_hosts |
{} |
hostname: IP entries for the runner and every job container, e.g. the Forgejo domain without NAT hairpin. |
forgejo_runner_timeout |
3h |
Job timeout. |
forgejo_runner_docker_path |
/docker/forgejo-runner |
Compose files (no secrets). |
forgejo_runner_secret_path |
/run/forgejo-runner |
Runner configs with secrets. Must be a tmpfs. |
forgejo_runner_uninstall |
false |
Stops the runners and removes their files. They stay registered in Forgejo. |
forgejo_runner_instances entries:
| Key | Required | Description |
|---|---|---|
name |
yes | Unique on the host, [a-z0-9-]. |
secret |
yes | 40 lowercase hex characters (openssl rand -hex 20), the same as on the Forgejo side. |
labels |
no | Overrides forgejo_runner_labels. |
cpus |
no | CPU limit of the runner's Docker daemon, which covers everything a job starts. |
memory |
no | Memory limit of the same, Compose syntax (4g). |
Usage
# Forgejo host (forgejo role)
forgejo_runners:
- name: runner-01
secret: "{{ vault_runner_01_secret }}"
# Runner host (this role)
forgejo_runner_url: https://git.example.com/
forgejo_runner_extra_hosts:
git.example.com: 10.0.0.2
forgejo_runner_instances:
- name: runner-01
secret: "{{ vault_runner_01_secret }}"
cpus: 2
memory: 4g
Notes
- Offline registration: the first 16 characters of the secret identify the runner, its UUID is their hex encoding. Changing only the last 24 characters rotates the secret.
- Changing a runner's config or compose file restarts it, which cancels a job that is running at that moment.
- Runners removed from
forgejo_runner_instancesare not stopped; setforgejo_runner_uninstallor stop the unit by hand. - The built-in Actions cache is off (it would keep data between jobs).
- The
dindcontainer is privileged. Jobs can't reach it directly, but run runners on a dedicated VM anyway. - Jobs can reach whatever the runner host can reach on the network. Restrict that with the firewall if needed.