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 # The runner daemon runs directly on indri and executes jobs on the host.
# via the tailscale-ci-gateway. This allows it to register with Forgejo # This avoids container networking complexity since it can reach Forgejo
# using the Tailscale URL, so job containers can also 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_data_dir: /Users/erichblume/.forgejo-runner
forgejo_runner_config_dir: /Users/erichblume/.config/forgejo-runner forgejo_runner_config_dir: /Users/erichblume/.config/forgejo-runner
forgejo_runner_log_dir: /Users/erichblume/Library/Logs forgejo_runner_log_dir: /Users/erichblume/Library/Logs
# Container settings # Runner registration - use localhost since we're running on indri
forgejo_runner_container_name: forgejo-runner forgejo_runner_instance_url: "http://localhost:3001"
forgejo_runner_image: code.forgejo.org/forgejo/runner:6.2.1 forgejo_runner_name: "indri-host-runner"
forgejo_runner_network: tailnet-jobs
# Runner registration - use Tailscale URL since we're on tailnet-jobs network # Labels format for host execution: label:host
forgejo_runner_instance_url: "https://forge.tail8d86e.ts.net" # Jobs run directly on the host, not in containers
forgejo_runner_name: "indri-docker-runner" forgejo_runner_labels: "ubuntu-latest:host,ubuntu-22.04:host"
# 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"
# Runner config # Runner config
forgejo_runner_capacity: 2 forgejo_runner_capacity: 2
forgejo_runner_timeout: 3h 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 # The runner daemon runs directly on indri using a locally compiled binary.
# via the tailscale-ci-gateway. Job containers also run on tailnet-jobs # Jobs execute on the host, reaching Forgejo at localhost:3001.
# and can reach Forgejo via Tailscale.
#
# DEPENDENCIES:
# - tailscale_ci_gateway role must run first (creates tailnet-jobs network)
- name: Ensure forgejo-runner directories exist - name: Ensure forgejo-runner directories exist
ansible.builtin.file: ansible.builtin.file:
@ -24,30 +20,21 @@
mode: '0644' mode: '0644'
notify: Restart forgejo-runner 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 - name: Check if runner is registered
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ forgejo_runner_data_dir }}/.runner" path: "{{ forgejo_runner_data_dir }}/.runner"
register: forgejo_runner_registered register: forgejo_runner_registered
- name: Register runner with Forgejo (via tailnet) - name: Register runner with Forgejo
ansible.builtin.command: ansible.builtin.command:
cmd: > cmd: >
docker run --rm {{ forgejo_runner_binary }} register
--network=container:tailscale-ci-gateway
-v {{ forgejo_runner_data_dir }}:/data
{{ forgejo_runner_image }}
forgejo-runner register
--instance "{{ forgejo_runner_instance_url }}" --instance "{{ forgejo_runner_instance_url }}"
--token "{{ forgejo_runner_token }}" --token "{{ forgejo_runner_token }}"
--name "{{ forgejo_runner_name }}" --name "{{ forgejo_runner_name }}"
--labels "{{ forgejo_runner_labels }}" --labels "{{ forgejo_runner_labels }}"
--no-interactive --no-interactive
chdir: "{{ forgejo_runner_data_dir }}"
when: not forgejo_runner_registered.stat.exists when: not forgejo_runner_registered.stat.exists
changed_when: true changed_when: true

View file

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

View file

@ -7,28 +7,20 @@
<string>mcquack.forgejo-runner</string> <string>mcquack.forgejo-runner</string>
<key>ProgramArguments</key> <key>ProgramArguments</key>
<array> <array>
<string>/bin/bash</string> <string>{{ forgejo_runner_binary }}</string>
<string>-c</string> <string>daemon</string>
<string><![CDATA[ <string>--config</string>
# Stop and remove existing container if present <string>{{ forgejo_runner_config_dir }}/config.yaml</string>
/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>
</array> </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> <key>RunAtLoad</key>
<true/> <true/>
<key>KeepAlive</key> <key>KeepAlive</key>