-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Ephemeral Action Runner (EPAR) keeps a warm pool of disposable GitHub Actions self-hosted runners on your own machines.
It is built for teams that want fast self-hosted Linux runners without keeping long-lived runner VMs around. EPAR creates an instance, registers it as an ephemeral GitHub runner, lets GitHub run one job on it, deletes the instance after that job, and creates a replacement.
flowchart LR
Workflow["GitHub Actions job"] --> Runner["Ephemeral runner"]
EPAR["EPAR supervisor"] --> Provider["Local provider<br/>Tart, WSL2, or Docker-DinD"]
Provider --> Runner
Runner --> Job["One job runs"]
Job --> Delete["Delete instance"]
Delete --> Replace["Create replacement"]
Replace --> Runner
- Disposable runners: every runner is expected to handle one job and then disappear.
-
Warm pool:
pool upkeeps ready runners online so jobs do not wait for a full image build. - Use spare hosts: turn a supported Mac, Windows, Linux, or Docker-capable machine into a pool of disposable Linux GitHub runners.
- Image control: the default image is runner-only, and extra tooling is added with explicit install scripts.
- Optional pull caching: point runner Docker daemons at a registry mirror service to reduce repeated Docker Hub image pull time when that is a meaningful part of the job.
- GitHub App auth: the host uses a GitHub App to request short-lived runner registration tokens.
EPAR adds cleanup and isolation around GitHub self-hosted runners, but it does not make an existing host safe for arbitrary untrusted workflows. The project is intended for trusted jobs where disposable instances reduce host pollution, stale runner state, and accidental cross-job interference.
GitHub's self-hosted runner warning still applies: GitHub recommends using self-hosted runners only with private repositories because public repository forks can run code on the runner machine through pull request workflows. Read the official GitHub guidance before exposing any self-hosted runner to public or untrusted workflows: Adding self-hosted runners.
For Docker-heavy Linux CI, start with Docker-DinD when the host already has a Docker runtime that supports privileged containers. It is usually the least intrusive path for existing workflows because jobs still get their own private Docker daemon, fixed Compose names and ports stay inside that runner instance, and the workflow can keep using explicit Docker platforms such as linux/amd64.
Use Tart or WSL when their host model is the reason you are choosing them: Tart for Apple Silicon VM-based isolation and macOS-host workflows, WSL for Windows-hosted Linux runners and native x64 Docker workloads on x64 Windows.
Choose the provider whose isolation model, CPU architecture, and Docker behavior match the workflows you plan to run.
| Machine you have | EPAR provider | Runner architecture | Notes |
|---|---|---|---|
| Docker-capable host | Docker-DinD | Host-selected Linux container platform | Recommended first choice for Docker Compose-heavy Linux CI when privileged containers are acceptable. Deleting the runner container deletes the job's inner Docker world. |
| Apple Silicon macOS | Tart | Ubuntu ARM64 | Good for Linux jobs that can run on ARM64. Can optionally run linux/amd64 Docker containers through Tart Rosetta when configured with a distinct label. |
| Windows with WSL2 | WSL2 | Ubuntu x64 | Good for Linux jobs and Docker workflows that pull linux/amd64 images. Use for trusted internal jobs unless your environment has accepted the WSL isolation model. |
Future providers can fit the same model: if EPAR supports the machine, that machine can contribute disposable runner capacity with its own labels.
EPAR does not include Docker, browsers, Node, or project tools in every image by default.
| Image style | Config | Includes |
|---|---|---|
| Runner-only base |
configs/tart.example.yml, configs/wsl.example.yml, or configs/docker-dind.example.yml
|
GitHub Actions runner and minimal runtime dependencies. Docker-DinD also includes Docker Engine for its private inner daemon. |
| Docker/browser | add scripts/guest/ubuntu/install-docker-browser.sh to image.customInstallScripts
|
Docker Engine, Docker CLI, Compose v2, Buildx, and a Chromium-compatible browser |
| Web/E2E |
configs/tart.web-e2e.example.yml, configs/wsl.web-e2e.example.yml, or configs/docker-dind.web-e2e.example.yml
|
Docker/browser plus Node.js/npm, zip, rsync, and mysql-client. The Tart example also enables Rosetta amd64 Docker validation. |
| Custom | add your own script path to image.customInstallScripts
|
Whatever your script installs inside Ubuntu |
Example:
image:
customInstallScripts:
- scripts/guest/ubuntu/install-web-e2e.sh
- examples/custom-install/install-extra-apt-tools.sh-
Build the CLI:
go build -o ./bin/ephemeral-action-runner ./cmd/ephemeral-action-runner
-
Create a GitHub App that can manage organization self-hosted runners. See docs/github-app.md.
-
Copy one example config into
.local/config.ymland fill in the GitHub App values.Docker-DinD, recommended for Docker-heavy Linux CI when your host Docker runtime supports privileged containers:
mkdir -p .local cp configs/docker-dind.example.yml .local/config.yml
macOS with Tart:
mkdir -p .local cp configs/tart.example.yml .local/config.yml
Windows with WSL2:
New-Item -ItemType Directory -Force .local Copy-Item configs/wsl.example.yml .local/config.yml
-
For WSL, export a clean Ubuntu 24.04 rootfs once:
New-Item -ItemType Directory -Force work/images wsl --install -d Ubuntu-24.04 --no-launch wsl --export Ubuntu-24.04 work/images/ubuntu-24.04-clean.rootfs.tar
-
Build the runner image:
./bin/ephemeral-action-runner image build --replace
If your provider is Docker-DinD, or if your selected install scripts use EPAR's Docker/browser or web/E2E scripts, first run:
./bin/ephemeral-action-runner image update-upstream
-
Verify two registered runners and clean them up:
./bin/ephemeral-action-runner pool verify --instances 2 --register-only --cleanup
-
Start a foreground pool:
./bin/ephemeral-action-runner pool up --instances 2
pool up is intentionally foreground. Keep it running while you want runners available. Stop it with Ctrl-C to clean up matching local instances and GitHub runner records.
sequenceDiagram
participant E as EPAR
participant P as Provider
participant R as Runner instance
participant G as GitHub
E->>P: clone and start instance
P-->>R: instance boots
E->>R: validate runtime
E->>G: request registration token
E->>R: configure runner as ephemeral
R-->>G: runner comes online
G->>R: assign one workflow job
R-->>G: runner exits after job
E->>P: delete instance
E->>G: remove stale runner record if needed
E->>P: create replacement
Cleanup is prefix-safe: EPAR only touches instances and GitHub runners whose names match pool.namePrefix.
Start with:
- Usage: commands for setup, image builds, verification, and pool operation.
- GitHub App Setup: minimum GitHub App permissions and config fields.
- Image Build: runner-only base images, install scripts, web/E2E images, and customization.
Provider details:
- Tart Provider: Apple Silicon macOS and Ubuntu ARM64.
- WSL Provider: Windows WSL2, rootfs tar images, and WSL caveats.
- Docker-DinD Provider: privileged runner containers with private Docker daemons.
Operational context:
- Design: lifecycle and liveness model.
- Operations: logs, cleanup, and troubleshooting.
- Security: trust boundaries and secret handling.
- Background: why Linux guests are preferred for Docker and Compose-heavy jobs.
- Docker Registry Mirrors: optional pull-through cache setup and private image cautions.
-
macOS Startup: Open at Login and LaunchAgent examples for starting
pool up. - Adding A Provider: provider interface expectations.
Tracked configs are examples only. Put real GitHub App IDs, private key paths, and local runner settings in .local/config.yml, configs/*.local.yml, or ~/.config/ephemeral-action-runner/config.yml; those paths are not intended for Git.
Generated from the main repository docs at b721d85. Edit README.md and docs/; the wiki copy is overwritten by automation.