Skip to content
AI Primer
release

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.

6 min read
Runner-up runs GitHub Actions on self-hosted machines
Runner-up runs GitHub Actions on self-hosted machines

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:

  1. Coordinator: Maintains a scale-set session for each repository and pool, and tracks waiting jobs.
  2. Place: A slot on a machine that runs one job at a time. Jobs remain queued at GitHub until a suitable place is available.
  3. Agent: Starts over SSH on the selected machine and creates the job's container, VM or account.
  4. 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 container tool. 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.

Further reading

Discussion across the web

Where this story is being discussed, in original context.

On X· 2 threads
Owned hardware2 posts
Coding-agent setup1 post
Share on X