--- # Binary forgejo_runner_version: "6.3.1" forgejo_runner_arch: "linux-amd64" forgejo_runner_url: "https://code.forgejo.org/forgejo/runner/releases/download/v{{ forgejo_runner_version }}/forgejo-runner-{{ forgejo_runner_version }}-{{ forgejo_runner_arch }}" forgejo_runner_bin_path: "/usr/local/bin/forgejo-runner" # Runtime forgejo_runner_user: "runner" forgejo_runner_dir: "/opt/forgejo-runner" forgejo_runner_config_path: "{{ forgejo_runner_dir }}/config.yml" forgejo_runner_labels: "docker:docker://node:20-bookworm,ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://node:20-bookworm,ubuntu-24.04:docker://node:20-bookworm" # --- config.yml contents ---------------------------------------------------- # These reproduce, exactly, the stock `generate-config` output this host has # been running since first deploy. They are variables now only because the file # became a template; changing one of them changes the box. forgejo_runner_log_level: info forgejo_runner_log_job_level: info forgejo_runner_capacity: 4 forgejo_runner_job_timeout: "3h" forgejo_runner_shutdown_timeout: "3h" forgejo_runner_cache_enabled: true # Empty = autodetect this host's outbound address. See the note in the template # before changing it; under dind this is the value that decides whether # actions/cache works. forgejo_runner_cache_host: "" forgejo_runner_container_network: "" forgejo_runner_valid_volumes: [] # Extra job environment and extra `docker run` options for job containers. # Both MERGE with what dind adds rather than replacing it. forgejo_runner_envs: {} forgejo_runner_container_options: "" # The Forgejo instance this runner registers with. forgejo_instance_url: "https://forgejo.contrapeso.xyz" # forgejo_runner_registration_token comes from the vault. # --- Health check ----------------------------------------------------------- # The check answers "is this service healthy" and records the answer two ways: # a log file, and its own exit code. The exit code is the durable artefact — # systemd stores it, so `systemctl is-failed forgejo-runner-healthcheck.service` # answers the question with no monitoring system involved at all. healthcheck_interval_seconds: 60 healthcheck_script_dir: /opt/forgejo-runner-healthcheck healthcheck_script_path: "{{ healthcheck_script_dir }}/forgejo_runner_healthcheck.sh" healthcheck_log_file: "{{ healthcheck_script_dir }}/forgejo_runner_healthcheck.log" healthcheck_service_name: forgejo-runner-healthcheck # WHERE TO REPORT HEALTH — the one place to plug in monitoring. # # Empty means "check, log, exit honestly, report nowhere". Set it to any URL # that accepts an HTTP ping and the check will report there. Nothing in this # role is specific to a particular monitoring product: the Uptime Kuma API # calls, monitor creation and token handling that used to live here are gone. # # A pull-based monitor (Prometheus node_exporter textfile, say) needs this left # empty — it reads the systemd unit state instead. healthcheck_push_url: "" # Bearer token for the Gatus external endpoint. Required whenever a push URL # is set: Gatus rejects an unauthenticated push with 401. healthcheck_push_token: "" # --- Docker-in-Docker for job containers ------------------------------------ # Off by default. The role default is the SAFE state: `container.docker_host` # stays "-", which is what this host has always run, and jobs get no Docker at # all. services/forgejo-runner/deploy_forgejo_runner_playbook.yml turns it on. # # The shape is the one in Forgejo's own admin docs, "Utilizing Docker within # Actions" -> Docker-in-Docker, and it is three moving parts, not one: # # 1. a docker:dind container on the HOST daemon, published on loopback only; # 2. container.docker_host, which points the RUNNER PROCESS at that daemon, # so job/service/step containers are created INSIDE dind; # 3. runner.envs.DOCKER_HOST plus a container.options --add-host, which is # what lets the JOB reach the same daemon from inside. # # Step 3 is not optional and not a duplicate of step 2. The two addresses are # different because they are used from different places: the runner talks to # 127.0.0.1 on the host, and a job container inside dind cannot - its own # 127.0.0.1 is itself. Setting only docker_host gets you job containers that # run in dind but still cannot find a daemon, which looks exactly like the bug # we are fixing. See the README. forgejo_runner_dind_enabled: false forgejo_runner_dind_container: forgejo-dind # Already present on forgejo-runner-box, and the tag the project's own workflow # refers to. A floating 28.x picks up patch releases on container recreation. forgejo_runner_dind_image: "docker:28-dind" forgejo_runner_dind_service: forgejo-dind # Loopback ONLY. This daemon has no TLS and no authentication, so anything that # can open the port is root inside a privileged container. 127.0.0.1 keeps that # to processes on this box; do not move it to 0.0.0.0 or the LAN address. # # The number is 2376 because the upstream docs use it. 2376 is conventionally # the TLS port and this is PLAINTEXT - the port number promises nothing. forgejo_runner_dind_bind_address: "127.0.0.1" forgejo_runner_dind_host_port: 2376 forgejo_runner_dind_container_port: 2375 # The name a JOB container uses to reach the dind daemon. Resolved by an # --add-host ... :host-gateway on the job container, which Docker expands in # the context of the daemon that CREATES the container - dind - so it lands on # dind's own inner bridge gateway, where its dockerd is listening. That is why # a name is used here and not an address: the address is only knowable from # inside dind, at container-creation time. forgejo_runner_dind_internal_host: "forgejo-dind.docker.internal" # Networks for the containers dind creates. act_runner makes a fresh network # per job, so these multiply, and Docker's stock pool reaches into 192.168.x - # the same space as this LAN. A job container that allocated 192.168.1.0/24 # would lose the route to every box on it, including this one. Pinning the pool # well clear of the LAN removes that entirely. forgejo_runner_dind_address_pool: "172.20.0.0/14" forgejo_runner_dind_address_pool_size: 24 # How long the dind unit waits for its daemon to answer before failing. Cold # start pulls the image and initialises /var/lib/docker. forgejo_runner_dind_ready_timeout: 180 # dind keeps its own /var/lib/docker. Without a volume it is wiped every time # the container is recreated, and every image every workflow uses - the # playwright image alone is 3.3GB - is pulled again from scratch. forgejo_runner_dind_volume: forgejo-dind-lib # ...which then grows without bound, so it is pruned. `until=168h` keeps # anything used in the last week, which is the cache that makes the volume # worth having, and drops the rest. # # Volumes are deliberately NOT pruned: `until` does not apply to them, so # --volumes here would be "delete every volume not currently attached", and a # workflow's named volume between two jobs is not an obvious thing to destroy # on a timer. Watch disk and prune those by hand. forgejo_runner_dind_prune_enabled: true forgejo_runner_dind_prune_on_calendar: "Sun 04:30" forgejo_runner_dind_prune_filter: "until=168h"