Skip to content

thornebridge/buildserv

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

buildserv

The Thornebridge GitHub Actions self-hosted runner. Lives on a dedicated Mac mini (Apple Silicon), serves all org repos — lend.works, banklyze, pm, ccs-marketing, anything tagged with the buildserv label.

🤖 Provisioning a new Mac mini? Paste docs/setup-agent-prompt.md into a Claude Code session on the new machine and the agent will walk through the entire setup. That doc is the canonical entry point — start there.

Why a repo

The previous buildserv was set up by hand on a Mac that died, taking its configuration with it. This repo is the explicit, version-controlled answer: anyone with the disk, an env file, and ./setup.sh can rebuild the entire setup in 30 minutes.

Topology

One Mac mini, two GitHub Actions runner processes registered against the thornebridge org:

Runner Labels Used for
<hostname>-light self-hosted, macOS, ARM64, buildserv, light Lint, typecheck, deploys, notify — anything cheap
<hostname>-heavy self-hosted, macOS, ARM64, buildserv, heavy Tests, RLS integration, Docker builds — resource-intensive

Running both lets independent jobs in the same workflow execute in parallel (e.g. build-images heavy + deploy-staging light overlap by ~10-15 min on a typical lend.works PR).

Setup

On a fresh Mac mini logged in as a non-root user:

# 1. Clone this repo
git clone https://github.com/thornebridge/buildserv.git /opt/buildserv-repo
cd /opt/buildserv-repo

# 2. Configure env
cp env.example $HOME/.buildserv.env
# Edit $HOME/.buildserv.env — see header comments for each var

# 3. Generate a fresh runner registration token (expires in 1 hour)
gh api -X POST /orgs/thornebridge/actions/runners/registration-token | jq -r .token
# Paste the token into $HOME/.buildserv.env as RUNNER_TOKEN

# 4. Run setup — idempotent, safe to re-run on errors
./setup.sh

After the first run completes, both runners should appear as Idle at https://github.com/organizations/thornebridge/settings/actions/runners.

Phases (run individually if you need to)

./setup.sh --phase system    # Xcode CLT + Homebrew + workspace dirs
./setup.sh --phase tools     # Node 22, pnpm 10, gh, jq, tmux, htop
./setup.sh --phase docker    # Docker Desktop + GHCR login + buildx
./setup.sh --phase runner    # Download + register both runners
./setup.sh --phase launchd   # LaunchAgents for auto-start
./setup.sh --phase cache     # Persistent pnpm + Buildx cache
./setup.sh --phase ops       # Weekly Docker prune, disable sleep, SSH

Operations

./scripts/status.sh        # Health snapshot — runner state, disk, cache sizes
./scripts/restart.sh       # Reload both LaunchAgents
./scripts/update.sh        # Pull latest actions-runner binary, restart
./scripts/unregister.sh    # Deregister from GitHub (use before retiring host)

Logs live in /opt/buildserv/logs/:

  • runner-light.{out,err} — light runner output
  • runner-heavy.{out,err} — heavy runner output
  • prune.{log,out,err} — weekly Docker prune

Tail them with tail -f /opt/buildserv/logs/runner-light.out.

Docker Desktop settings (manual, one-time)

A few Docker Desktop settings can't be set from the CLI. The first time you launch Docker.app, configure:

  • General → Use Rosetta for x86_64/amd64 emulation on Apple Silicon: ON
    Native amd64 cross-compilation; 2-3× faster than the default QEMU.
  • Resources → CPUs: max
  • Resources → Memory: ≥ 8 GB (12 GB on a 16 GB mini)
  • Resources → Swap: 2 GB
  • Resources → Disk image size: ≥ 80 GB
  • Resources → File sharing: add /opt/buildserv so Buildx cache works
  • Builders → containerd image store: ON

What lives where

/opt/buildserv/
├── runners/
│   ├── light/           # GH Actions runner install (light labels)
│   └── heavy/           # GH Actions runner install (heavy labels)
├── cache/
│   ├── pnpm-store/      # Shared pnpm store across all builds
│   ├── buildx/          # Symlinked into /tmp/.buildx-cache-*
│   └── turbo/           # Turbo remote cache (if used)
└── logs/                # Runner + prune logs

/opt/buildserv-repo/ holds this repo (the source). The setup script keeps these two paths separate so you can git pull updates without touching live runner state.

Recovery from a dead mini

# On the replacement mini, after logging in:
git clone https://github.com/thornebridge/buildserv.git /opt/buildserv-repo
cd /opt/buildserv-repo
cp env.example $HOME/.buildserv.env
# Fill in RUNNER_TOKEN, GHCR_TOKEN — see header
./setup.sh

If the dead mini's disk is recoverable, restoring /opt/buildserv/cache/ to the new mini will give the first builds a warm cache (~10× faster Docker layer reuse).

Why two runners on one machine

GitHub Actions self-hosted runners are single-threaded: one runner = one concurrent job. Two runners on the same hardware means our build-images (heavy) and deploy-staging (light) can overlap in the same workflow, cutting wall-clock pipeline time meaningfully on every PR.

License

Proprietary — Thornebridge LLC.

About

GitHub Actions self-hosted runner setup for Thornebridge — Mac mini buildserv, idempotent provisioning, light+heavy runner split.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors