RunZero Documentation
Everything you need to know about configuring, running, and scaling your local GitHub Actions runner fleet with Docker containers, dedicated Linux Virtual Machines, and intelligent proxy caching.
Quick Start in 60 Seconds
RunZero deploys an autoscaler daemon alongside local caching proxies (apt-cacher-ng for Debian/Ubuntu, Verdaccio for npm, Athens for Go, and Docker Hub mirror).
# 1. Clone the repository
git clone git@github.com:el-j/run-zero.git
cd run-zero
# 2. Launch the interactive configuration wizard
make env
# 3. Build runner images (ARM64 + AMD64)
make build
# 4. (Optional for VM speeds) Build golden VM base template
make build-vm-base
# 5. Start the autoscaler and caching proxies in the background
make start
# 6. Stream live runner logs
make logs Prerequisites
RunZero runs on macOS, Linux, and Windows with minimal system requirements:
- Container Runtime: OrbStack (recommended for macOS), Docker Desktop, or native Linux Docker.
- GitHub Personal Access Token (PAT): Classic PAT with
reposcope (andadmin:orgif managing an Organization fleet). - Hardware: Apple Silicon (M1/M2/M3/M4) or Intel/AMD x86_64 CPU, 4GB+ RAM.
Interactive Setup Wizard (make env)
Running make env invokes an interactive terminal wizard that asks for your token, account username, desired engine, and concurrency limit:
If run non-interactively (e.g. in CI with NON_INTERACTIVE=true), make env automatically copies .env.example to .env without blocking.
Real-Time Web Dashboard & Host VM Bridge
RunZero features a built-in, lightweight real-time Web UI Dashboard and Host VM Bridge. Monitor active ephemeral runners, GitHub API quotas, hybrid routing distributions, and cache storage with zero external SaaS dependencies.
1. Real-Time Web Dashboard (Port 49505)
The Web Dashboard starts automatically alongside the autoscaler daemon on http://localhost:49505.
# Quick launch in your default web browser
make dashboard
# Or start the full fleet and navigate directly to:
open http://localhost:49505 Key Dashboard Capabilities
- Live Ephemeral Runner Fleet: Real-time status halos, active duration stopwatches, target repo/workflow name, and runtime engine (Docker vs OrbStack / WSL2 / Multipass VM).
- Server-Sent Events (SSE) Stream: Instant UI updates delivered via
/api/eventswith zero page polling and zero socket bloat. - GitHub API Quota Tracker: Visual gauge tracking live GitHub REST API rate-limit headroom.
- Hybrid Routing Breakdown: Real-time counts of jobs routed to Docker vs VM backends (Services/DIND, Browser E2E, Systemd).
- Proxy Cache Analytics: Disk usage breakdown across NPM (Verdaccio), Go (Athens), APT, Docker, and Toolcache directories, with one-click purge actions.
- Streaming Terminal Log Viewer: Live tailing ring buffer of autoscaler daemon logs with auto-scroll and pause controls.
Dashboard Configuration Options
| Variable | Default | Description |
|---|---|---|
DASHBOARD_ENABLED | true | Enables the built-in HTTP and SSE dashboard server. |
DASHBOARD_HOST | 0.0.0.0 | Host address the dashboard binds to. |
DASHBOARD_PORT | 49505 | Web UI dashboard listening port. |
2. Host VM Bridge (Port 49504)
When running RunZero's autoscaler inside a Docker container (make start-container), native virtualization CLI tools like orbctl, wsl.exe, or multipass are not directly executable within the container. The Host VM Bridge bridges this gap by exposing a zero-dependency HTTP control daemon on the host.
# Start the Host VM Bridge daemon on the host
make bridge-start
# Check bridge daemon status and health
make bridge-status
# Stream bridge logs
make bridge-logs
# Stop the bridge daemon
make bridge-stop Bridge Configuration Options
| Variable | Default | Description |
|---|---|---|
HOST_VM_BRIDGE_PORT | 49504 | Port the host bridge HTTP daemon listens on. |
HOST_VM_BRIDGE_URL | http://host.docker.internal:49504 | URL the containerized autoscaler uses to reach the host bridge. |
Dual-Engine Fleet Architecture
Unlike traditional runner solutions that force you into a single execution model, RunZero offers a pluggable driver architecture:
🪐 OrbStack Linux VM Driver (RUNNER_BACKEND=orbstack-vm)
Spawns dedicated lightweight Linux machines via OrbStack and Apple Virtualization.framework. Provides full systemd support, unconfined Google Chrome sandboxing, background daemons, and internal Docker-in-VM capabilities. Automatically connects to host Verdaccio, Athens, and apt-cacher proxies via host.orb.internal.
🏗️ Golden VM Base Image (make build-vm-base)
To make VM boot times near-instant, make build-vm-base pre-bakes the shared toolchain into a golden base image (runzero-vm-base-<arch>). Subsequent VM jobs spin up in seconds by cloning the base image!
🐳 Docker Container Driver (RUNNER_BACKEND=docker)
Launches ultra-fast ephemeral containers powered by Docker Engine. Starts in ~0.3 seconds and consumes zero RAM when idle. Ideal for unit tests, linting, build pipelines, and JavaScript/Python workflows.
🪟 Windows WSL2 Driver (RUNNER_BACKEND=wsl2)
Runs native Linux runners directly inside Windows Subsystem for Linux 2 (Windows 10/11 and Windows Server) without requiring Docker Desktop.
🚀 Canonical Multipass Driver (RUNNER_BACKEND=multipass)
Universal cross-platform virtual machine backend using Canonical Multipass via QEMU, Hyper-V, or VirtualBox.
Hybrid Auto-Routing
With RUNNER_BACKEND=auto and AUTO_ROUTE_VM=true, you don't need to choose a single engine. The autoscaler inspects workflow labels and job names:
# Standard unit test runs in ultra-fast Docker container (~0.3s)
jobs:
test:
runs-on: [ self-hosted, local ]
steps:
- run: npm test
# Lighthouse / Browser E2E automatically routes to dedicated Linux VM!
jobs:
audit:
runs-on: [ self-hosted, local, browser ] # Triggers: 'browser', 'e2e', 'lighthouse', 'systemd', 'vm'
steps:
- run: npx lhci autorun APT & Package Proxy Caching
RunZero runs lightweight background caching proxy services so that neither your container image builds nor your CI runner executions waste bandwidth re-downloading packages from public registries:
| Proxy Service | Target Packages | Host Port | Web UI / Dashboard |
|---|---|---|---|
| APT Cacher NG | Ubuntu/Debian .deb packages | :49503 | http://localhost:49503/acng-report.html |
| Verdaccio | NPM, Yarn, PNPM JavaScript packages | :49501 | http://localhost:49501 |
| Athens | Go language modules | :49500 | http://localhost:49500 |
| Docker Registry Mirror | Docker Hub container layers | :49502 | http://localhost:49502 |
| devpi | Python pip & uv packages (PyPI) | :49507 | http://localhost:49507/root/pypi/+simple/ |
| kellnr | Cargo / Rust crates (crates.io) | :49506 | http://localhost:49506 |
Cargo has no single "index URL" environment variable the way pip/uv (PIP_INDEX_URL/UV_INDEX_URL)
and Go (GOPROXY) do -- pointing it at kellnr requires a real ~/.cargo/config.toml
source-replacement block, written automatically for both the Docker and OrbStack VM engines. pip also refuses a
plain-HTTP index on any host other than localhost/127.0.0.1 unless it's explicitly
trusted via PIP_TRUSTED_HOST, which both engines set automatically alongside PIP_INDEX_URL
wherever the index isn't reached via localhost.
OrbStack VM Local Disk Caching
The proxies above cache package downloads over the network. Package managers also keep a local,
already-extracted disk cache (~/.npm, ~/.cache/pip, ~/go/pkg,
~/.cargo/registry, etc.), which for the Docker engine is a plain -v host:container
bind mount. An OrbStack VM is a real, separate guest filesystem with no such flag, but every non-isolated
OrbStack VM automatically virtiofs-shares the entire host macOS filesystem into the guest at a fixed path,
/mnt/mac<absolute-macOS-path>. RunZero's OrbStack driver uses this to bind-mount each
host-side cache directory onto its container-style destination path inside the VM before the job runs, so
cache data written by one ephemeral VM is really on host disk and visible to the next VM cloned for the same
architecture.
First-Level Dependencies & Upstream Tools
RunZero builds upon best-of-breed open-source virtualization and runtime platforms:
- OrbStack: Superfast, lightweight container engine and Linux Virtual Machine hypervisor for macOS.
- Docker Engine & Compose: Industry standard container runtime and service orchestration.
- apt-cacher-ng: High-performance caching proxy for Debian and Ubuntu APT package downloads.
- Verdaccio: Lightweight zero-config private npm proxy registry with web interface.
- Athens: Enterprise-grade Go module caching proxy server.
- NVM (Node Version Manager): Multi-version Node.js environment pre-baking Node 20, 22, and 24.
- Microsoft .NET SDK: Cross-platform .NET 8.0 SDK runtime for C# and F# builds.
- Playwright: Reliable end-to-end testing for modern web apps with pre-installed OS dependencies.
Zombie Runner Healing & Auto-Reconciliation
If an ephemeral runner machine is stopped abruptly mid-job (e.g. during workstation sleep or make stop), GitHub might mark it as offline but busy: true.
RunZero's autoscaler automatically reconciles zombie runners by cancelling stranded runs and unsticking GitHub's runner queue so jobs never hang.
Environment Variables Reference (.env)
| Variable | Default | Description |
|---|---|---|
ACCESS_TOKEN | Required | GitHub Personal Access Token (classic with repo scope). |
OWNER | Required | GitHub username to monitor for auto-discovery. |
RUNNER_BACKEND | auto | Execution engine: auto, docker, orbstack-vm, wsl2, multipass. |
AUTO_ROUTE_VM | true | Automatically route jobs with browser/e2e/systemd labels to VMs. |
RUNNER_ARCH | both | Target architecture: both (ARM64 + AMD64), arm64, amd64. |
ACTIVE_REPO_DAYS | 60 | Only monitor repositories with commits in the last N days. |
MAX_RUNNERS | 4 | Maximum concurrent runner instances allowed on this workstation. |
PROXIES_ENABLED | true | Start apt-cacher, Verdaccio, Athens, and Docker Hub caching proxies. |
Makefile Command Reference
make start # Start Autoscaler and Proxy registries (apt-cacher, Verdaccio, Athens)
make stop # Stop Autoscaler, Proxies, and cleanup active runners
make status # Show status of services and active runner instances
make logs # Stream live autoscaler logs
make test # Run 85 unit tests directly (0.04s)
make test-suite # Run Flake8 linter, Mypy type-checker, and Pytest coverage
make install-hooks # Install pre-commit quality guard into .git/hooks/pre-commit
make pre-commit # Run pre-commit quality guard manually with auto-fixes
make lint # Run Flake8 linter and Mypy static type checker
make lint-fix # Auto-fix Python formatting and strip trailing whitespace
make build-vm-base # Build golden OrbStack VM base image for instant cloning
make website-dev # Start Astro documentation website development server
make website-build # Build Astro static website and synchronize to docs/
make verdaccio-ui # Open Verdaccio NPM registry dashboard (http://localhost:49501)
make apt-cacher-ui # Open apt-cacher-ng statistics report (http://localhost:49503/acng-report.html)
make clean-cache # Clear shared package caches to reclaim disk space Troubleshooting & FAQ
How do I test a workflow on Apple Silicon and AMD64?
Set RUNNER_ARCH=both in .env and specify runs-on: [self-hosted, local, arm64] or runs-on: [self-hosted, local, amd64] in your GitHub workflow YAML.
How does package caching work across containers and VMs?
RunZero exposes apt-cacher-ng on port 49503, Verdaccio on port 49501, and Athens on port 49500. Containers on host networking and VMs on virtualized bridges automatically route package downloads through these local proxies.