Skip to content
github-actions[bot] edited this page Aug 11, 2026 · 10 revisions

Usage

Use this page for normal EPAR tasks. Docker Sandboxes is the primary provider on Linux, macOS, and Windows hosts when its capability checks pass; the documentation hub links to compatibility and provider-specific guides.

Prerequisites

Task Required tool or access
Run a source archive Go 1.25 or newer, or Docker for the no-Go controller builder
Register or inspect GitHub runners A GitHub App with organization self-hosted runner read/write permission
Docker Sandboxes (primary) Docker and the sbx CLI with at least one diagnostic pass and zero failures; the wizard builds and imports the selected EPAR template
Docker Container (compatibility) Docker with privileged Linux-container support; reveal it in the wizard with C. Show compatibility providers
WSL2 (compatibility) Native Windows, WSL2, and Docker when preparing the default WSL image; reveal it in the wizard with C. Show compatibility providers
Tart (retired) Existing configurations only; runtime and exact cleanup compatibility remain available without onboarding

Get the source from the EPAR releases page, extract the source archive, and work from that folder. You do not need Packer, GitHub CLI, or sshpass.

EPAR works with a Docker installation that supports the selected provider. Docker Sandboxes is the default path; compatibility providers are explicit choices and EPAR never silently changes a configured provider.

Start a pool

On macOS, Linux, Git Bash, or native Windows PowerShell, run:

./start

PowerShell resolves ./start to EPAR's internal Windows wrapper. Do not use bare start in PowerShell because that name is the Start-Process alias. The wrapper uses local Go when available, otherwise it uses Docker, to build a native controller under .local/bin/<os>-<arch>/; it then executes that project-local controller directly. See Running EPAR Without Installing Go for the fallback details.

When .local/config.yml is absent and the terminal is interactive, ./start launches the same first-run wizard as init. It asks for the GitHub App and an explicit runner group. The runner-group list orders GitHub's Default group first, hides blocked groups and policy details initially, and lets you reveal either from the menu. Four provider identities remain accepted at runtime, while three are onboarding-capable: 1. Docker Sandboxes — recommended (default) is visible first; choose C. Show compatibility providers to reveal 2. Docker Container and 3. WSL2; Tart remains a retired runtime/configuration identity with no onboarding option. Docker Sandboxes remains the default even when its prerequisite status is unavailable. Pressing Enter or choosing 1 while it is unavailable displays its status and remediation, then keeps the menu open so you can make an explicit available choice; choose R to refresh prerequisites. Storage does not make a provider unavailable. Docker Sandboxes presents four artifact choices: EPAR verified prebuilt Full (default), EPAR verified prebuilt Act, Catthehacker Ubuntu Full built locally with a 30–60 minute warning, and Catthehacker Ubuntu Act built locally. Docker Container and WSL retain their two local built-in profiles plus another validated Catthehacker tag. The wizard writes empty custom-install scripts, weekly updates at 07:00 local time, and host-trust overlay for applicable providers; edit the generated config afterward to change those advanced settings. Later option lists use 0 to go back, text prompts use /back, and a final provider-neutral review shows the applicable artifact estimate before one creation decision. Running ./start init exits after writing, while an embedded first run continues through the ordinary image/template provisioning and pool startup path. When sbx is installed, the wizard runs sbx daemon start --detach before Docker Sandboxes diagnostics so a stopped daemon does not require a manual retry.

See Docker Sandboxes for source profiles, capacity, local receipts, and platform validation status.

Create or choose configuration

Create configuration without starting runners:

./start init

Pass a config path and an instance count through the wrapper:

./start --config .local/ci.yml --instances 2

For an unattended controller that should remain alive through transient external-service outages, opt in explicitly:

./start --config .local/ci.yml --external-outage-retry=continuous
./start --config .local/ci.yml --external-outage-retry=4h

The accepted values are off, continuous, or a positive Go duration. Omission is off, so ordinary interactive starts keep their existing fail-fast startup behavior. The flag applies only to the configured start lifecycle; it does not retry the first-run wizard, wrapper controller builds, pool up, or maintenance commands.

On Windows PowerShell, backslash paths may be clearer while the command remains the same:

./start --config .local\ci.yml --instances 2

If --instances is omitted, start, pool up, and pool verify use pool.instances from the selected config. EPAR resolves configuration from --config, EPAR_CONFIG, .local/config.yml, then ~/.config/ephemeral-action-runner/config.yml. Tracked files in configs/ are examples; keep App values and key paths in an ignored local file. See Configuration for every setting and Runner Group Security before broadening repository access.

Multiple configs from the same checkout may run concurrently when they use different canonical config paths, unique pool.namePrefix values, unique workflow-routing labels, and preferably separate log directories. EPAR rejects a second controller for the same config path or prefix before provisioning or cleanup can mutate provider state. Config-scoped BuildKit builders and transient workspaces keep divergent registry, trust, and cache settings isolated.

Storage-consuming commands fail before their provider side effects when a measured capacity domain cannot retain storage.minimumFree after the operation's largest phase-overlapping allocation. An unavailable capacity measurement is reported as a warning and does not block the command; the checkout filesystem is not assumed to contain provider runtime storage. Inspect the same calculation, including unknown domains and their causes, with ./start storage status --operation <name>; preserve --config and --project-root for non-default configurations. The one-invocation --allow-insufficient-storage option permits only confirmed insufficient-capacity admission to continue; provider diagnostics, GitHub policy, ownership, lifecycle, cleanup protections, remote Docker-context rejection, and malformed storage topology remain enforced. The option is available on start, pool up, pool verify, image update, image build, and image update-upstream, including the equivalent ./start ... wrapper forms.

Each normal start also reconciles interrupted exact-owned work and retires unreferenced superseded artifacts after replacement readback. Use ./start storage status to inspect the result. ./start storage prune --legacy previews prefix-era resources, which remain manual and require the displayed plan hash before execution.

Large disposable downloads and temporary build data live under .local/cache; compact receipts and lifecycle state live under .local/state; builder and ownership metadata live under .local/storage; logs live under work/logs. See Generated files and recovery before manual deletion. For a last-resort fresh start of one stopped configuration, run ./start storage reset --config <path>, review its exact targets, then repeat with --execute --plan <hash>.

Press Ctrl-C once to stop a foreground pool, then wait for cleanup to finish before closing the terminal. Use --keep-on-exit only to retain owned resources for deliberate debugging.

Update runner artifacts

By default, EPAR checks mutable source-image tags and runnerVersion: latest weekly at 07:00 local time. The first-run wizard writes this default; edit image.updateFrequency and image.updateTime afterward to choose daily, every two weeks, monthly, manual, or another local time. Local image settings, script or certificate content, platform, EPAR assets, and missing or corrupt artifacts always apply on the next start without waiting for the schedule.

Force an immediate remote check without forcing a rebuild:

./start image update

Manual policy means this command triggers remote checks. ./start image build remains the force-build path. A running ephemeral pool checks when due, drains only after busy jobs finish, activates the verified replacement, and restores pool capacity; persistent runners record the update for the next process start.

Verify before sending jobs

Verify one disposable runner without GitHub registration:

./start pool verify --instances 1 --cleanup

Verify registration and online/idle state:

./start pool verify --instances 2 --register-only --cleanup

--cleanup removes verification resources after the check. Docker Sandboxes uses its exact ownership records; compatibility providers use the configured pool-name boundary, and retired Tart configurations keep their existing exact runtime/cleanup behavior. Use Operations for the distinction and recovery guidance.

Run, inspect, and clean up

start is the normal command because it checks the reusable image or template first. pool up is for a pool you have deliberately prepared:

./start pool up --instances 2
./start status
./start cleanup

Use status --no-github or cleanup --no-github when you intentionally need to skip GitHub runner status or deletion. pool down is an alias for cleanup.

For a command-construction preview on compatible providers, add --dry-run:

./start pool verify --dry-run --instances 1

Docker Sandboxes intentionally does not support dry-run instance creation because EPAR must read back the exact active template-cache identity. Use its admission and template checks instead.

Target the right runner

GitHub matches every value in runs-on against a runner's labels. The smallest workflow selector is:

runs-on: [self-hosted]

Add a provider or workload label to avoid routing work to the wrong environment:

runs-on: [self-hosted, linux, epar-docker-container-catthehacker-ubuntu]

EPAR adds an epar-host-<machine> label by default. Use it only when a job must target one specific host. Give each independent pool in the same organization a unique pool.namePrefix; this is also its cleanup boundary.

Common next tasks

Clone this wiki locally