Runner-up runs GitHub Actions on self-hosted machines
Runner-up packages a self-hosted setup used for thousands of daily CI jobs, with disposable VMs for isolation. Its author estimates equivalent cloud runners would cost $5,000–$9,000 per month.

TL;DR
- Runner-up publishes a self-hosted GitHub Actions setup already handling thousands of daily jobs on private repositories, according to kunchenguid's announcement.
- Equivalent cloud runners would cost its author an estimated $5,000–$9,000 a month; his calculation put owned hardware an order of magnitude cheaper.
- One abstraction covers Linux, macOS and Windows machines, with Tart and Hyper-V among the backends described in the author's follow-up.
- VM-backed runners can be destroyed after each job, as the author explained; the repository also offers containers, Windows accounts and direct host execution.
The README counts macOS VMs started outside runner-up against its two-VM limit. The Windows backend has another wrinkle: Hyper-V places run Linux guests, while Windows jobs use fresh Windows accounts.
Owned hardware
Kunchenguid released runner-up as a free, MIT-licensed tool after using the setup for his own CI workload. His $5,000–$9,000 monthly figure is an estimate of equivalent cloud costs, rather than a bill he says he previously paid.
His fleet includes an M5 MacBook Pro, an M4 Mac mini and a gaming PC, all machines with other uses that often sit idle. Renting VPS capacity also works, he said in a follow-up, but he estimated ownership would remain cheaper for sustained workloads lasting more than a few months.
The backyard-cluster crowd was represented too: andrew_n_carr expressed interest in adding it to an existing cluster.
SSH and runner scale sets
Runner-up uses GitHub's standalone scale-set client, a Go library extracted from Actions Runner Controller. Scale sets can therefore supply the GitHub-facing machinery without requiring Kubernetes.
The architecture separates coordination from execution:
- Coordinator: Maintains a scale-set session for each repository and pool, and tracks waiting jobs.
- Place: A slot on a machine that runs one job at a time. Jobs remain queued at GitHub until a suitable place is available.
- Agent: Starts over SSH on the selected machine and creates the job's container, VM or account.
- GitHub runner: Executes one job and communicates directly with GitHub after startup.
The coordinator runs on Linux or macOS, even when workers run Windows. A place's runner process holds an open-file lock, allowing an active job to retain its slot through coordinator restarts.
An unreachable machine stops receiving work until its agent responds again. The README describes cleanup and a new runner after a place crashes, rather than preserving the failed execution's in-memory state.
Linux, macOS and Windows providers
The provider requirements distinguish the machine hosting a job from the environment executing it:
- Linux: Docker containers, KVM virtual machines or direct host execution.
- Apple-silicon Macs: Fresh Tart clones for macOS, or Linux guests through Apple's
containertool. Macs also support direct host execution. - Windows: Fresh standard user accounts for Windows jobs, or Hyper-V VMs running Linux. A runner-up service running as SYSTEM creates the accounts and VMs.
The mixed-OS abstraction is the release's best feature. Its providers have substantially different maturity levels, detailed below.
Pool labels
A pool is the label a workflow requests through runs-on. The setup instructions show separate labels for Linux and macOS:
There is a routing gotcha: runner-up machine add assigns a new machine's places to every pool currently listed unless a pool is specified. Adding a Mac to a setup containing only my-linux can therefore make that Mac eligible for Linux-labelled jobs.
The configuration explicitly assigns pools to places; the label itself supplies no operating-system enforcement. runner-up check-config validates the setup, while runner-up status reports places, reservations and waiting work.
Resource scheduling
The scheduling model budgets shared machines rather than treating every runner slot as interchangeable:
- Owner headroom: Machines reserve one core and 1,024 MB for their owner by default.
- Pool sizes: A pool can specify the cores and memory each job reserves. Without a size, the job keeps the place's existing share.
- Waiting-job reservation: The first-ranked job blocked only on resources keeps room on one machine until it fits.
- Mac capacity: At most two macOS VMs run simultaneously, including VMs launched outside runner-up.
Order rules choose work across repositories, but ordering within one repository's pool is approximate because GitHub selects which job a runner ultimately receives. The README also describes live configuration reloads about a second after an edited file settles, without stopping active jobs.
Disposable jobs and fork protection
VM-backed execution gives each job an environment that is removed afterward. Kunchenguid described that cleanup in the announcement replies:
The provider definitions make an important distinction: direct host places reuse the machine, and Windows account places create users rather than VMs. A fresh runner registration does not make those environments equivalent isolation boundaries.
Fork-originated pull-request jobs are refused by default across every provider. For GitHub authentication, the coordinator asks gh auth token whenever it opens a session; runner-up's setup file contains no token, while sign-in remains managed by gh.
Coding-agent setup
The quick start includes a pasteable prompt for a coding agent to install runner-up, check SSH access, configure pools and register the coordinator as a service. It requires the agent to ask before using sudo and leaves Windows administrator setup as a human-confirmed step.
In the announcement discussion, kunchenguid also said he answers suspected AI-generated replies when the response might help other people.
Experimental backends
The repository's status section draws a sharper boundary than the cross-platform feature list:
- Docker and KVM on Linux, plus Tart and host execution on Macs: Have run real jobs.
- Hyper-V: Has run test jobs from a check, but not anyone's daily workload.
- Apple containers: Have started the runner with stand-in registration, without a real job.
- Windows accounts: Pass tests but have not run a real job.
- Live configuration changes: Tested end to end with substitutes for GitHub, SSH and Docker, rather than real jobs.
Hyper-V, Apple containers and Windows accounts are explicitly experimental, alongside borrowing a virtual Mac and draining places. The underlying scale-set client remains in public preview; GitHub describes its API as stable while warning that client interfaces and examples may change.