GuidesLocal GitHub runners (macOS VM)

Local GitHub runners (macOS VM)

Running GitHub Actions on your own macOS VM is one of the best ways to avoid “renting your own infrastructure”.

This guide shows a practical setup using CiderStack + GitHub’s self-hosted runner, and an ephemeral runner workflow using snapshots/clones.

What you’ll build

  • A macOS VM that registers as a self-hosted GitHub Actions runner
  • Optional: a golden image + ephemeral clones (one clean runner per job)

Before you start

  • Apple limitation: only 2 macOS VMs can run concurrently per physical Mac (Apple platform rule). To scale runners, you scale by adding Macs.
  • Security: treat self-hosted runners as sensitive—prefer org-level runners with tight permissions, and strongly prefer ephemeral runners for untrusted repos/PRs.

Option A (simple): one “always-on” runner VM

1) Create and boot a VM

Using the CLI:

# Create a VM (use a template or image you have)
cider vm create gh-runner --template "Xcode-15"
# Or: cider vm create gh-runner --image sonoma-arm64 --cpu 4 --memory 8
 
# Start headless (no window; good for CI)
cider vm start gh-runner --headless
 
# Wait for an IP, then SSH in
cider vm ip gh-runner --wait 120
cider ssh gh-runner

2) Install and configure the GitHub runner inside the VM

In your GitHub repo (or org), go to:

  • Settings → Actions → Runners → New self-hosted runner

Then follow GitHub’s instructions inside the VM (they provide the exact download + config commands).

Recommended runner labels:

  • macos
  • ciderstack
  • apple-silicon
  • xcode-15 (or whatever matches your VM image)

3) Run it as a service

Use GitHub’s “run as a service” instructions so the runner survives reboots and comes up automatically.

4) Target it from workflows

In your workflow:

runs-on: [self-hosted, macos, ciderstack]

This pattern gives you a clean runner per job and makes teardown easy.

1) Build a “golden runner” VM

Create a VM, install macOS if needed (cider install myvm --latest), then inside the VM install Xcode and the GitHub runner prerequisites. Do not permanently register the runner if you plan to clone.

Create a snapshot so you can restore or clone from it:

cider snapshot create runner-base "runner-clean"
# Or use a template: cider template create "CI Runner Base" --vm runner-base

2) For each job: clone → register → run → destroy

High-level flow:

  1. Clone from your golden VM: cider vm clone runner-clean runner-job-123
  2. Start and get IP: cider vm start runner-job-123 --headless, then cider vm ip runner-job-123 --wait 120
  3. SSH in, register with GitHub using a short-lived registration token, run the job
  4. Teardown: de-register the runner, then cider vm destroy runner-job-123 -f

This is the safest approach for PRs and minimizes state drift. For automation, use cider vm create ... --intent ci --ttl 2h so VMs can auto-retire.

3) Scaling out

Because Apple limits concurrency per host, scaling is typically:

  • Add more Macs
  • Spread runners across the fleet (Fleet Manager helps here; it’s currently in beta with early access for Small Batch)

Common gotchas

  • Docker inside the macOS VM: Docker Desktop needs nested virtualization; macOS doesn’t support it today.
  • Keychain / codesigning: signing identities and notarization credentials need extra care—keep secrets scoped, prefer ephemeral VMs, and avoid baking secrets into images.
  • Updates: update Xcode and macOS in the golden image, then re-snapshot.

Next