Private-only, re-checked
Only repos that are private, owned by you, and of type User are dispatched; forks are excluded by default. Privacy is re-verified immediately before every mint and fails closed.
v0.4 · Linux + Windows validated
runnerize is a stateless, on-demand pool of self-hosted GitHub Actions runners for your private repos. One always-on dispatcher mints a just-in-time runner per queued job inside a throwaway rootless container, runs exactly one job, then auto-deregisters. No standing runners. No host credentials. Nothing persists.
# run the dispatcher — pulls straight from GitHub
npx github:animeshkundu/runnerize run
or install globally
npm i -g runnerize
Node ≥ 18 · zero dependencies
Every job runs in a fresh container from a fat image. No cross-job state, no persistent-workspace collisions, no long-lived runner credentials left on disk.
Only your own private, non-fork user repos are ever dispatched. Privacy is re-checked immediately before every mint and fails closed on any doubt.
It scales the number of runners to match queued demand and lets GitHub assign the jobs — the correct model for ephemeral runners, never pinned to a job id.
How it works
The dispatcher watches your owned private repos. Every poll it counts queued jobs whose labels a flavor can serve, mints just enough just-in-time runners to meet demand, and hands each one a throwaway container. The runner takes exactly one job, deregisters itself, and the container is destroyed. The job never sees your host credentials or caches.
Quickstart
You need Node ≥ 18, a container runtime for the default linux flavor (native podman/docker
on Linux, WSL2 on Windows, Colima or podman machine on macOS), and a GitHub token
resolved from $GH_TOKEN / $GITHUB_TOKEN, else gh auth token.
# start the dispatcher (foreground)
node bin/runnerize.js run
# see flavors, private repos, live runners
node bin/runnerize.js status
# enumerate demand, mint nothing
node bin/runnerize.js run --dry-run
# one reconcile / cleanup pass
node bin/runnerize.js remove
# install as a boot service
node bin/runnerize.js service install
jobs:
build:
runs-on: [self-hosted, linux, x64]
Always name the OS and arch labels. A bare [self-hosted] is ambiguous across
flavors, so runnerize maps it to the default linux flavor only.
--max <n> | max concurrent runners (default 4) |
--interval <ms> | poll interval (default 15000; adapts with repo count) |
--idle-timeout <ms> | kill an unclaimed runner after this (default 120000) |
RUNNERIZE_LINUX_IMAGE | fat image (default catthehacker/ubuntu:full-latest) |
RUNNERIZE_RUNNER_DIR | use a preinstalled runner dir instead of downloading |
RUNNERIZE_WSL_DISTRO | pin the WSL distro (Windows host) |
Security model
Only repos that are private, owned by you, and of type User are dispatched; forks are excluded by default. Privacy is re-verified immediately before every mint and fails closed.
Each job runs in a throwaway rootless container that cannot read the host keychain, credential file, or process memory. Validated end to end: a job could not read a host secret.
The token is held only by the dispatcher and passed to the runner via env, never on a command line. The runner binary is SHA-256-verified before use. A short-lived, actions:write-scoped GitHub App is the recommended production credential.
Backends
Linux is live-validated with rootless containers. The Windows Sandbox backend is validated on Windows 11 24H2. The macOS tart backend is implemented, with Apple Silicon hardware validation pending.
| Flavor | Host | Mechanism | Status |
|---|---|---|---|
linux |
any — Linux, WSL, Colima | rootless container, fat image | working |
windows |
Windows 11 Pro / Enterprise | Windows Sandbox (disposable) | working · Win11 24H2 validated |
macos |
Apple Silicon | tart VM |
preview · hardware validation pending |
Honest limitations
Roadmap
actions:write only, no code access — as the default production auth.tart backend.npx github:animeshkundu/runnerize#<tag> and npm.Run private-repo CI on your own machine, one ephemeral runner at a time.
npx github:animeshkundu/runnerize run