runnerctl 0.3.0
runnerctl
Run several GitHub Actions runner pools on one Docker host. No Kubernetes. A fresh container for every job.
For developers and small teams who already have a Linux server, a Mac, or a Windows PC and want to manage runner capacity, images, labels, and credentials from one CLI. Pools can belong to different GitHub organizations or individual repositories. Runner containers receive short-lived registration tokens; PATs and GitHub App private keys stay with the manager.
Example status:
POOL SCOPE INTENT TARGET ACTIVE IDLE BUSY STARTING UNKNOWN DRAINING
validate example-team running 2 2 1 1 0 0 0
build octocat/project running 1 1 0 1 0 0 0
The manager restores state after a restart, replaces completed runners, and preserves running jobs during normal stopping and upgrades. Capacity is configured per pool. This is a single-host manager for trusted workflows; pool labels and shared Docker access do not isolate tenants.
Supported platforms
Runners are always Linux containers; on macOS and Windows they run in Docker's Linux VM. The manager and CLI run on:
See macOS and Windows for setup, and the compatibility record for tested versions and limits.
Install with Cargo
On the Docker host, install Rust/Cargo 1.85 or later and a C compiler/linker. Docker must be running, and your user must be able to access its local socket. On a Mac or Windows PC, read macOS and Windows first. The commands below work the same in PowerShell and Command Prompt.
The CLI comes from crates.io. image pull downloads the matching versioned runner image from GHCR; releases build images for Linux amd64 and arm64. Add ~/.cargo/bin (%USERPROFILE%\.cargo\bin on Windows) to PATH if needed. Repeat cargo install runnerctl --locked to update the CLI.
Your first runner
Create one organization pool with one runner:
auth login reads a PAT without echoing it. A fine-grained organization PAT needs Self-hosted runners: Read and write, plus any organization approval. See authentication for GitHub Apps and automation.
service install starts a systemd user service on Linux and WSL2, a LaunchAgent on macOS, or a scheduled task on Windows. On Linux, keep it running after logout by enabling lingering with sudo loginctl enable-linger "$USER". You can also run runnerctl manager in another terminal. Commands and the manager must use the same --home directory. The default is ~/.config/runnerctl, or %LOCALAPPDATA%\runnerctl on Windows.
Add this workflow to a repository accessible to the organization runner, then run it from GitHub Actions:
name: runnerctl smoke test
on: workflow_dispatch
jobs:
smoke:
runs-on:
steps:
- uses: actions/checkout@v4
- run: echo 'runnerctl is ready'
After the job, status shows a replacement with a new runner ID.
For an individual repository, use init --repo OWNER/REPO and pool add ci --repo OWNER/REPO --auth OWNER --labels ci. A fine-grained repository PAT needs Administration: Read and write for that repository. Organization and repository pools can coexist.
Daily commands
Normal stop drains runners. Replacements stop immediately, busy jobs finish, and idle runners can accept one final job before removal. --wait waits for cleanup; its timeout never escalates to cancellation. Use --force when you explicitly want to terminate existing work. Status explains whether draining runners are waiting for a job, finishing a job, or awaiting cleanup.
To enable Docker builds, job containers, and services, create a pool with --docker-socket. The provided image shares the host network and maps its runtime/workspace to matching host paths. This requires trusted workflows. Check the workflow compatibility matrix.
CPU and memory limits apply to each runner container. Sibling job/service containers and Docker builds use the host daemon and need their own resource limits.
Documentation
- Authentication: PATs and GitHub Apps
- macOS and Windows
- Operations: configuration, draining, recovery, updates, and custom images
- Workflow compatibility and real GitHub checks
- Example configuration
- Tests and release procedure
Development
# Use --image local/runnerctl-test-base:1 when adding a development pool.
For migration, start with a single runner and a unique label, verify your workflows, then move capacity from your previous setup. Existing v0.1 organization/PAT configurations and saved state remain readable. Existing pool image references are preserved; choose the new image explicitly when upgrading a pool.
Licensed under MIT. Contributions and reproducible bug reports are welcome; see CONTRIBUTING.md.
