runaway

// self-hosted github actions orchestration

Your runners.
Your servers.
No middleman.

An open-source orchestrator for ephemeral GitHub Actions runners. Drop it on the VMs you already own, point it at your GitHub orgs, and get clean scale-to-zero CI — no Kubernetes cluster, no SaaS owning your compute.

BSL 1.1 · runs on any docker host · no inbound port

scale-set / ci-linux live
rnr-7f3a
rnr-2c9e
rnr-b14d
rnr-e80a
rnr-44f1
rnr-9ab6
rnr-1d2c
rnr-c057
rnr-3e9f
one job each · then gone AutoRemove: true

// why runaway exists

Self-hosting your runners is the right call. Every other way to do it makes you give something back.

Kubernetes wants a cluster. Hosted runner services want your compute back on their cloud. A hand-rolled docker run --rm works — until you need a second host, or until someone reaches for restart: unless-stopped and your runners quietly rot into stateful pets. Runaway is the missing middle: true-ephemeral runners, a real dashboard, and multi-host orchestration, on hardware you already own.

// the hub

Every runner, every host, on one screen.

Connect your GitHub orgs, define scale sets, and the orchestrator does the rest — reconciling desired state against live Docker on every host, spawning and reaping runners so you never babysit a container. The dashboard is the cache; Docker is the truth.

runaway · overview
Runaway dashboard showing live jobs, capacity, scale sets, hosts, and cost savings Runaway dashboard showing live jobs, capacity, scale sets, hosts, and cost savings

// ephemeral by design

One job per runner. Then it’s gone.

Every runner is AutoRemove: true — it runs exactly one job, then the container is destroyed and replaced. No accumulated state, no writeable-layer bloat, no “why is this runner acting weird” at 2am. One reconciliation loop is the only thing that touches Docker, so crash recovery is just re-reading reality.

runaway · runners
Runners table showing ephemeral containers across scale sets, mixed busy and idle Runners table showing ephemeral containers across scale sets, mixed busy and idle

// multi-host

Add a box from anywhere. No inbound port.

Each host runs one small agent that dials out to the hub over an authenticated WebSocket — your homelab NAS, a Hetzner VM, a spare Mac mini. No open ports, no SSH, no Tailscale, no cluster. The hub holds no Docker socket at all; every host is reached through its own agent, NAT-friendly by construction.

runaway · hosts
Hosts view showing three online hosts with CPU, RAM, disk, and load gauges Hosts view showing three online hosts with CPU, RAM, disk, and load gauges

// scale to zero

Scale clean to zero. Or hold a warm floor.

Runaway watches GitHub for queued and in-progress jobs and sizes each pool to demand. Set a floor of zero to scale all the way down between jobs, or keep a few warm so the next push doesn’t cold-start. Polling needs no public exposure; opt-in webhooks drop latency to near-instant when you want it.

runaway · scale set
Scale set configuration showing min and max runners, runtime, cache, and labels Scale set configuration showing min and max runners, runtime, cache, and labels

// your hardware, your bill

Free CI on the machines you already own.

Self-hosted runner minutes are free — Runaway just makes them reliable and hands-off. The built-in cost view tallies what the same jobs would have cost on GitHub-hosted runners, so the spread is there at a glance, conservatively, against the lowest hosted tier.

runaway · insights
Cost dashboard showing monthly savings versus GitHub-hosted runners, broken down by organization Cost dashboard showing monthly savings versus GitHub-hosted runners, broken down by organization

// where it fits

Built for the gap between a bash script and a Kubernetes cluster.

Runaway ARC (k8s) Hosted SaaS DIY systemd
Runs without Kubernetes
Compute stays on your hardware
True-ephemeral by default
Multi-host from one UI
Autoscale to zero
Web dashboard + job history
No vendor control plane
Setup measured in minutes days minutes hours

vs / actions-runner-controller

The right tool at enterprise scale, but it runs on Kubernetes: you own a cluster, its upgrades, and the expertise to secure it. Runaway is a compose stack on a Linux box.

vs / hosted runner services

Actuated and RunsOn are fast, but each puts a vendor back in the loop — their control plane, or their product in your cloud. Runaway is the orchestration, open-source, on hardware you already have.

vs / systemd + docker run --rm

Works for one host in one person’s head. No UI, no autoscaling, no multi-host placement, no job history — and one restart: unless-stopped away from bloat. Runaway is that same primitive with orchestration on top.

// how it works

You declare the shape. The reconciler holds it.

01

Connect an org

Add a GitHub org or user with a classic PAT. Runaway validates scopes and discovers your repos.

02

Define a scale set

Pick a host (or many), a min/max, a runtime, and caches. That’s your desired state.

03

Push code

Runaway sees the queued job, spawns an ephemeral runner on a host with room, and GitHub dispatches to it.

04

Walk away

The job finishes, the container is destroyed, the pool returns to its floor. The loop never stops watching.

// built to be trusted

Open source, and architected like it.

BSL 1.1 → Apache 2.0

Source-available today; every release converts to Apache 2.0 after three years. Non-competitor use is granted now.

Socket-less hub

The hub holds no Docker socket. A hub compromise hands an attacker no root on your boxes — all compute lives on agents behind your NAT.

Secrets encrypted at rest

PATs and registry passwords are AES-256-GCM encrypted with a key that never leaves your env. Decrypted secrets are never logged.

Telemetry: opt-out, anonymous

Anonymous counts only — no org names, no repos, no IPs. One click to disable, and the full field list is published.

One-way trust

The hub can trigger an agent to update, but never ships it code. A future hosted control plane can’t push executables to your hosts. Ever.

Stop babysitting runners.

Drop it on a box you own. Two containers, one compose file, an admin account, and you’re taking jobs in about five minutes.

Clone the repo, generate three secrets, and bring up the stack:

RUNAWAY_MASTER_KEY=$(openssl rand -base64 32)
BETTER_AUTH_SECRET=$(openssl rand -base64 32)
RUNAWAY_LOCAL_AGENT_TOKEN=$(openssl rand -base64 32)

docker compose -f docker-compose.example.yml up -d

Open http://localhost:3000, create your admin account, add an org + PAT, and create a scale set. The hub and a co-located agent run as one compose stack.