Image for the org's self-hosted GitHub Actions runners on ger3 (runs-on: [self-hosted, ger3]),
deployed as Coolify service h4esa4ronft1iqni8yrlu8ze. Built weekly to
ghcr.io/ericdahl-dev/gh-runner:noble.
It is myoung34/github-runner:ubuntu-noble plus the system packages our CI needs (Chrome, libpq,
libvips, wkhtmltopdf's libraries, shellcheck). Ruby, Node and Python come from the setup-* actions.
When a repo needs something else: install it in that job if only one repo needs it, add it to the
Dockerfile once two do. macOS and Windows jobs stay on GitHub-hosted runners.
Two things these runners can't do, because jobs run Docker through the host's socket:
container:jobs andservices:with host ports. Start Postgres in the runner's own network instead (--network container:$HOSTNAME); see ericdahl-ops.github/workflows/ci.yml.- Isolation: a job has root on ger3. The runner group is limited to private repos for that reason.
Runners keep repo checkouts and gem/node caches in /tmp/github-runner/ger3-N between jobs.
host/clean-runner-workdirs.sh empties them every Sunday at 04:00 UTC. It is installed on ger3 as
/usr/local/bin/clean-runner-workdirs with /etc/cron.d/clean-runner-workdirs, and logs to
/var/log/clean-runner-workdirs.log. Edit it here and copy it back to ger3; the host doesn't pull it.
host/ci-load-sample.sh runs every 5 minutes on ger3 and appends the load average and each
runner's busy state to /var/log/ci-load.tsv (35 days kept). host/ci-load-report.sh emails a 7-day
summary every Monday at 13:00 UTC, from alerts@ericdahl.dev through the mail-bridge container
(ger3 blocks outbound SMTP). Its settings are in /etc/ci-load-report.env (root only:
MAIL_BRIDGE_SMTP_PASSWORD from Doppler ericdahl-dev/prd, and REPORT_TO). Cron:
/etc/cron.d/ci-load-report, log /var/log/ci-load-report.log. With GH_BILLING_PAT in the env file
(fine-grained, org Administration: read, Doppler GH_BILLING_PAT) the email also shows private-repo
GitHub-hosted minutes for the month against the 3,000 included.
- Runners: Coolify service
h4esa4ronft1iqni8yrlu8zeon ger3, two ephemeral org runnersger3-1andger3-2withcpu_shares: 256so the apps on ger3 win CPU contention. The registration token is the fine-grained PATGH_RUNNER_PATin Doppler, copied to the service'sACCESS_TOKEN. - A new image only reaches the runners on a Coolify restart with
?latest=true, and a restart kills running jobs: wait until both runners are idle. - In workflows: no
services:with host ports and no job-levelcontainer:. Start Postgres as a step withdocker run --network "container:$HOSTNAME"and remove it in anif: always()step (ericdahl-ops #254 is the reference). Putger3in cache keys, because caches saved on GitHub-hosted runners don't restore here. Give test jobstimeout-minutes. - The Sunday cleanup pauses each runner, so Docker shows it unhealthy for a few minutes. That's expected.