Switch forgejo-runner to host execution mode
All checks were successful
Test CI / test (pull_request) Successful in 4s

Docker-based runner had networking issues reaching Forgejo from job
containers. Host execution mode runs the runner daemon directly on indri,
with jobs executing on the host. Actions that need Docker use host
networking to access localhost:3001.

- Runner binary compiled locally at ~/code/3rd/forgejo-runner
- Labels use :host suffix instead of :docker://image
- PATH set in launchd plist for mise-managed tools (node, etc.)
- Container network set to "host" for actions needing Docker

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-01-24 13:23:39 -08:00
commit cfe5c0c0dd
4 changed files with 34 additions and 78 deletions

View file

@ -1,42 +1,23 @@
---
# Forgejo Runner - containerized daemon on tailnet-jobs network
# Forgejo Runner - host execution mode
#
# The runner daemon runs in a Docker container with access to the tailnet
# via the tailscale-ci-gateway. This allows it to register with Forgejo
# using the Tailscale URL, so job containers can also reach Forgejo.
# The runner daemon runs directly on indri and executes jobs on the host.
# This avoids container networking complexity since it can reach Forgejo
# at localhost:3001 directly.
forgejo_runner_binary: /Users/erichblume/code/3rd/forgejo-runner/forgejo-runner
forgejo_runner_data_dir: /Users/erichblume/.forgejo-runner
forgejo_runner_config_dir: /Users/erichblume/.config/forgejo-runner
forgejo_runner_log_dir: /Users/erichblume/Library/Logs
# Container settings
forgejo_runner_container_name: forgejo-runner
forgejo_runner_image: code.forgejo.org/forgejo/runner:6.2.1
forgejo_runner_network: tailnet-jobs
# Runner registration - use localhost since we're running on indri
forgejo_runner_instance_url: "http://localhost:3001"
forgejo_runner_name: "indri-host-runner"
# Runner registration - use Tailscale URL since we're on tailnet-jobs network
forgejo_runner_instance_url: "https://forge.tail8d86e.ts.net"
forgejo_runner_name: "indri-docker-runner"
# Labels format: label:docker://image
#
# Job containers also run on tailnet-jobs network and can reach:
# - forge.tail8d86e.ts.net for git clone
# - registry.tail8d86e.ts.net for container push/pull
#
# Bootstrap mode (use upstream images until we build ci-base):
# docker-builder:docker://docker:27-cli
# ubuntu-latest:docker://catthehacker/ubuntu:act-22.04
#
# Production mode (use our own images from zot):
# docker-builder:docker://registry.tail8d86e.ts.net/blumeops/ci-base:latest
# ubuntu-latest:docker://registry.tail8d86e.ts.net/blumeops/ci-base:latest
#
forgejo_runner_labels: "docker-builder:docker://docker:27-cli,ubuntu-latest:docker://catthehacker/ubuntu:act-22.04,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04"
# Labels format for host execution: label:host
# Jobs run directly on the host, not in containers
forgejo_runner_labels: "ubuntu-latest:host,ubuntu-22.04:host"
# Runner config
forgejo_runner_capacity: 2
forgejo_runner_timeout: 3h
# Docker container settings for jobs
forgejo_runner_privileged: true # Needed for container builds

View file

@ -1,12 +1,8 @@
---
# Forgejo Runner - containerized daemon on tailnet-jobs network
# Forgejo Runner - host execution mode
#
# The runner daemon runs in a Docker container with access to the tailnet
# via the tailscale-ci-gateway. Job containers also run on tailnet-jobs
# and can reach Forgejo via Tailscale.
#
# DEPENDENCIES:
# - tailscale_ci_gateway role must run first (creates tailnet-jobs network)
# The runner daemon runs directly on indri using a locally compiled binary.
# Jobs execute on the host, reaching Forgejo at localhost:3001.
- name: Ensure forgejo-runner directories exist
ansible.builtin.file:
@ -24,30 +20,21 @@
mode: '0644'
notify: Restart forgejo-runner
- name: Pull forgejo-runner image
ansible.builtin.command:
cmd: docker pull {{ forgejo_runner_image }}
register: forgejo_runner_pull
changed_when: "'Downloaded newer image' in forgejo_runner_pull.stdout or 'Pull complete' in forgejo_runner_pull.stdout"
- name: Check if runner is registered
ansible.builtin.stat:
path: "{{ forgejo_runner_data_dir }}/.runner"
register: forgejo_runner_registered
- name: Register runner with Forgejo (via tailnet)
- name: Register runner with Forgejo
ansible.builtin.command:
cmd: >
docker run --rm
--network=container:tailscale-ci-gateway
-v {{ forgejo_runner_data_dir }}:/data
{{ forgejo_runner_image }}
forgejo-runner register
{{ forgejo_runner_binary }} register
--instance "{{ forgejo_runner_instance_url }}"
--token "{{ forgejo_runner_token }}"
--name "{{ forgejo_runner_name }}"
--labels "{{ forgejo_runner_labels }}"
--no-interactive
chdir: "{{ forgejo_runner_data_dir }}"
when: not forgejo_runner_registered.stat.exists
changed_when: true

View file

@ -3,15 +3,11 @@ log:
level: info
runner:
# Path inside the container (data dir mounted at /data)
file: /data/.runner
file: {{ forgejo_runner_data_dir }}/.runner
capacity: {{ forgejo_runner_capacity }}
timeout: {{ forgejo_runner_timeout }}
# Even in host execution mode, some actions run in containers.
# Use host networking so containers can access localhost services.
container:
# Use tailnet-jobs network so job containers can reach Forgejo via Tailscale gateway
network: "{{ forgejo_runner_network }}"
privileged: {{ forgejo_runner_privileged | lower }}
# Mount Docker socket so jobs can build containers
valid_volumes:
- /var/run/docker.sock
network: "host"

View file

@ -7,28 +7,20 @@
<string>mcquack.forgejo-runner</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string><![CDATA[
# Stop and remove existing container if present
/usr/local/bin/docker stop {{ forgejo_runner_container_name }} 2>/dev/null || true
/usr/local/bin/docker rm {{ forgejo_runner_container_name }} 2>/dev/null || true
# Run the forgejo-runner daemon in a container
# - Uses gateway's network namespace for tailnet access (to poll Forgejo)
# - Mounts docker socket to spawn job containers
# - Mounts config and data directories
exec /usr/local/bin/docker run --rm \
--name {{ forgejo_runner_container_name }} \
--network=container:tailscale-ci-gateway \
--user root \
-v {{ ansible_env.HOME }}/.docker/run/docker.sock:/var/run/docker.sock \
-v {{ forgejo_runner_config_dir }}/config.yaml:/config.yaml:ro \
-v {{ forgejo_runner_data_dir }}:/data \
{{ forgejo_runner_image }} \
forgejo-runner daemon --config /config.yaml
]]></string>
<string>{{ forgejo_runner_binary }}</string>
<string>daemon</string>
<string>--config</string>
<string>{{ forgejo_runner_config_dir }}/config.yaml</string>
</array>
<key>WorkingDirectory</key>
<string>{{ forgejo_runner_data_dir }}</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/Users/erichblume/.local/share/mise/shims:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<key>HOME</key>
<string>/Users/erichblume</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>