James Valencia
← Projects

2026 — present

Scalable self-hosted GitHub Actions runners

Author / Open Source

Impact

Scaling from 1 to N runners becomes changing a number in the command, not editing a YAML file with N copy-pasted blocks — and a crash no longer leaves a ghost runner occupying a slot on GitHub.

DockerDocker ComposeGitHub ActionsShell

The problem

Declaring N self-hosted GitHub Actions runners by hand means N identical services copy-pasted into a docker-compose.yml. On top of that, a crash or a redeploy can leave a container registered on GitHub as available even though it no longer exists — a "zombie" runner GitHub keeps trying to use.

The approach

A single Docker Compose service, parameterized with the repo URL, scales with docker compose up --scale github-runner=N. Combined with an EPHEMERAL=true mode, each runner registers for a single job and deregisters itself once it's done, which solves the zombie runner problem at the root. Built to avoid depending on GitHub-hosted runners when the job needs a specific environment, or simply to avoid paying for CI minutes on repos with heavy builds.

Result

Scaling from 1 to N runners becomes changing a number in the command, not editing a YAML file with N copy-pasted blocks — and a crash no longer leaves a ghost runner occupying a slot on GitHub.