Skip to content

solutionforest/ephemeral-action-runner

Repository files navigation

Ephemeral Action Runner

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["Your host machine<br/>macOS, Windows, Linux, or Docker host"]
  Provider --> Runner
  Runner --> Job["One job runs"]
  Job --> Delete["Delete instance"]
  Delete --> Replace["Create replacement"]
  Replace --> Runner
Loading

Why Use EPAR

  • Disposable runners: every runner is expected to handle one job and then disappear.
  • Warm pool: pool up keeps 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.

Security Model

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.

Choosing A Provider

Start with the operating system or host tool you already have. The provider name is just the way EPAR creates and deletes the disposable Linux runner on that machine.

If your Mac, Windows PC, or Linux server already has Docker installed and supports privileged containers, try Docker-DinD first. In this project, Docker-DinD means "Docker-in-Docker": EPAR starts each runner as a Docker container, and that runner gets its own private Docker daemon inside it. This is usually the easiest first choice for Docker-heavy Linux CI because workflow containers, Compose projects, names, ports, and volumes stay inside that one disposable runner.

Use Tart when you specifically want Apple Silicon macOS to run each Linux runner as a lightweight virtual machine. Tart is the macOS VM tool EPAR uses under the hood; users do not need to know Tart before reading this README, just that this option is for Apple Silicon Macs.

Use WSL2 when your host is Windows and you want runners inside Windows Subsystem for Linux. WSL2 is Microsoft's Linux environment for Windows, and EPAR uses it to create disposable Ubuntu-based runners.

Choose the row that matches the machine you already have:

Host you have EPAR provider What that means Notes
macOS, Windows, or Linux with Docker installed Docker-DinD Each runner is a Docker container with its own Docker daemon 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 Each runner is an Ubuntu ARM64 virtual machine on your Mac 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 enabled WSL2 Each runner is an Ubuntu x64 WSL distro on your Windows machine 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.

Image Choice

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

Quick Start

  1. Build the CLI:

    go build -o ./bin/ephemeral-action-runner ./cmd/ephemeral-action-runner
  2. Create a GitHub App that can manage organization self-hosted runners. See docs/github-app.md.

  3. Copy one example config into .local/config.yml and fill in the GitHub App values.

    If your macOS, Windows, or Linux host already has Docker and supports privileged containers, start with Docker-DinD:

    mkdir -p .local
    cp configs/docker-dind.example.yml .local/config.yml

    If you are on an Apple Silicon Mac and want VM-based runners, use Tart:

    mkdir -p .local
    cp configs/tart.example.yml .local/config.yml

    If you are on Windows and want WSL2-based runners, use WSL2:

    New-Item -ItemType Directory -Force .local
    Copy-Item configs/wsl.example.yml .local/config.yml
  4. If you chose WSL2 on Windows, 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
  5. 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
  6. Verify two registered runners and clean them up:

    ./bin/ephemeral-action-runner pool verify --instances 2 --register-only --cleanup
  7. Start a foreground pool:

    ./bin/ephemeral-action-runner pool up --instances 2

How The Pool Behaves

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
Loading

Cleanup is prefix-safe: EPAR only touches instances and GitHub runners whose names match pool.namePrefix.

Documentation

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:

Operational context:

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.

About

Keeps a warm pool of disposable GitHub Actions self-hosted runners on your own machines

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors