The project's README from github.com/rhobuild/runpool. As one markdown file: README.md.
Runpool is a Docker-native control plane for autoscaling ephemeral CI runners on a single, capacity-bounded host. It translates provider demand into durable assignments, isolated per-job execution capsules, and optional repository-scoped cache lanes — without requiring Kubernetes.
flowchart LR
provider["GitHub Actions scale sets"] -->|"durable deliveries"| controller["Runpool controller"]
controller -->|"Moby API"| host["Docker Engine 28+"]
host --> capsule["Per-job capsule"]
host --> gateway["Per-job egress gateway"]
capsule -->|"DNS and HTTP proxy"| gateway
capsule --> runtime["Runner + dockerd + job containers"]
state[("SQLite state")] --- controller
cache[("Repository cache lane")] -. "optional" .- capsule
The controller has no inbound service dependency. It maintains outbound provider sessions, reconciles durable state with Docker, and admits work only while scheduling and disk budgets are healthy. See the architecture guide for package boundaries, identity, recovery, and capacity semantics.
Runpool is a resource-management, environment-hygiene, and network-policy boundary for CI workloads approved by the operator.
[!WARNING] Runpool is not a hostile code sandbox. It bounds and cleans up what an approved workload does; it does not contain code you would not have run.
Where that boundary ends:
--jitconfig interface makes the one-run JIT bundle
observable to the assigned workload, although it is kept out of controller
persistence, Docker metadata, logs, and later capsules;shared-daemon deliberately shares the Engine's compromise domain with the
platform and its services; dedicated-daemon reduces that blast radius but
is not a VM boundary;Under public-internet-only, direct egress is denied. Proxy-aware HTTP
clients can use absolute-form requests or CONNECT to allowed addresses on
ports 80 and 443. CONNECT is an opaque tunnel; Runpool applies destination
and port policy, not application-layer inspection. git+ssh, direct TCP,
non-DNS UDP, IPv6, and other ports fail closed. Read the complete
threat model before deployment.
Prerequisites are Go 1.26.0 or newer and a Linux host with rootful Docker Engine 28. The selected release-qualification target is documented in the support matrix.
git clone https://github.com/rhobuild/runpool.git
cd runpool
go build -trimpath -o runpool ./cmd/runpool
./runpool config validate --file internal/config/testdata/example.yaml
go test ./...
That builds the controller and checks a configuration. Reaching a job takes three more things: the two images, a target on GitHub, and a workflow that names the tier.
docker build -f build/capsule/Dockerfile -t runpool-capsule:dev .
docker build -f build/controller/Dockerfile -t runpool:dev .
[!NOTE]
runpool-capsule:devis the exact name a development build looks for. A release binary carries its capsule by digest and refuses to be pointed elsewhere; from source, this tag is the pairing.
Then a configuration, a credential, and the state directory:
RUNPOOL_GITHUB_URL=https://github.com/<owner>/<repo> \
RUNPOOL_GITHUB_TOKEN_FILE=/run/secrets/runpool/token \
RUNPOOL_HOST_TOPOLOGY=dedicated-daemon \
RUNPOOL_STATE_DIR="$PWD/state" \
./runpool doctor
RUNPOOL_STATE_DIR is $PWD/state here rather than the default
/var/lib/runpool/state, which the deployed controller uses from inside
its container and which a shell on the host cannot create without root.
doctor proves the host and the credential, and prints the runs-on
label for every tier it would serve. Then ./runpool serve, and a
workflow in that repository:
jobs:
build:
runs-on: runpool-standard
The complete example workflow shows what a job gets: its own Docker daemon, and egress through the relay. Deployment covers the GitHub side — which target shapes exist, what a runner group has to grant, and which credential to use — and the runbook covers operating it.
The reference Compose deployment asks for a released digest and refuses to start with no image at all; that it is a digest rather than a moving tag is yours to hold, and deployment covers verifying one. The images above are how a source checkout runs.
[!CAUTION] Do not use a production credential for local experimentation. A token that can reach real repositories reaches them from a host you are still testing.
Runpool provides a canonical Docker Compose manifest for its headless controller: no domain, reverse-proxy route, or public port is required.
[!IMPORTANT] A version is release-qualified only after a no-skip qualification run on the exact platform frozen before its candidate in
build/platform.lock.json. Each release carries the record that run produced, bound to its commit and to the image digests it publishes; release readiness lists the gates and the evidence each one leaves.
| Platform | Entry point |
|---|---|
| Docker Compose | deploy/compose/compose.yaml |
| A Compose platform | Deploy the canonical Compose file; a platform catalog entry, where the platform has one, derives its template from that same contract |
Read the deployment guide before installing. The
Compose contract reads an operator-managed
configuration file; the example selects
shared-daemon for a host shared with a platform, and illustrates an explicit reserve for
colocated services. Measure that reserve before deployment. Choose
dedicated-daemon only on an exclusive CI host.
| Guide | Purpose |
|---|---|
| Deployment | Compose, platforms, credentials, and lifecycle |
| Product contract | Guarantees, exclusions, and compatibility surfaces |
| Architecture | Components, dependency direction, state, and recovery |
| Configuration | Supported settings and validation rules |
| CLI reference | Generated commands, flags, and exit-code behaviour |
| Support matrix | Engine compatibility, release reference, and provider scope |
| Runbook | Operations, recovery, backup, GC, and uninstall |
| Threat model | Trust boundary, defences, one known weakness, and accepted exposure |
| ADRs | Architectural decisions and measured constraints |
| Release readiness | The objective gates a release must satisfy |
Read CONTRIBUTING.md before opening a pull request. Report security issues privately as described in SECURITY.md; never open a public issue for a suspected vulnerability.
Runpool is maintained by Rhobuild and licensed under the Apache License 2.0.