Skip to content

muhac/actions-runner-pool

Repository files navigation

🪉 gharp — GitHub Actions Runner Pool

A self-hosted, Docker-based pool of ephemeral GitHub Actions runners.

🍴 Dogfooded — this repo's own CI was running on a gharp pool during development!

✨ Features

  • 🔐 Self-hosted — no external service dependency
  • ♻️ Ephemeral runners — one job per runner, clean environment every time
  • Autoscaling — runners are created on-demand from webhook events
  • 📊 Built-in dashboard — inspect jobs and runners (with prometheus metrics), retry/cancel controls, and in-place credential rotation (webhook secret / pem / client secret) gated behind a ALLOW_ADMIN_EDIT kill-switch
  • 📦 Multi-repository, personal-account support — share compute across repos (not supported natively by GitHub)

👉 Try the dashboard live — same UI, mock data, no backend. Click Cancel / Retry to see how the dashboard reacts.

🚀 Quick Start

1. Run gharp

Pre-built multi-arch image: muhac/gharp.

Minimal docker run

docker run -d --name gharp \
  -p 8080:8080 \
  -e BASE_URL=https://gharp.example.com \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v gharp-data:/data \
  muhac/gharp:1

BASE_URL must be a public HTTPS URL GitHub can reach, terminating at the container's port 8080 (above mapped to the host's 8080). See docs/configuration.md for the full env-var reference.

Recommended — Docker Compose

# copy docker-compose.yml from this repo, then:
BASE_URL=https://gharp.example.com docker compose up -d

See docker-compose.yml for the full reference configuration (workdir cleanup, host Docker socket forwarding for ephemeral runners, runner-container log caps, and ADMIN_TOKEN passthrough — set the token in .env).

2. Create the GitHub App

Open ${BASE_URL}/setup and click Create GitHub App. gharp drives the GitHub App Manifest flow and persists the credentials locally.

⚠️ Don't rename the auto-generated App name on GitHub. gharp creates the App as gharp-<hash>; renaming it changes the slug, which breaks the install link gharp renders on /setup. The webhook keeps working — only the install link goes stale. To fix, delete the App on GitHub and re-run /setup.

⚠️ BASE_URL is sticky. It's baked into the GitHub App's webhook and OAuth-callback URLs at /setup time. Changing it later won't reconfigure the App — gharp will log a BASE_URL drift warning at startup. To migrate, re-run /setup (creating a fresh App) or revert BASE_URL to the original value.

3. Install the App

Pick the repos (or "All repositories") you want runners for and submit.

⚠️ Self-hosted runners + public repos = remote code execution. GitHub explicitly recommends against using self-hosted runners with public repositories: any contributor who can open a PR can run arbitrary code on your machine. Only install the App on private repos you trust, and run gharp on a dedicated VM / cloud instance / homelab node. gharp drops public-repo workflow_job webhooks by default. To opt in, set ALLOW_PUBLIC_REPOS=true for all public repos, or use REPO_ALLOWLIST=owner/repo to bypass the guard for selected public repos.

4. Add a workflow

jobs:
  build:
    runs-on:
      - self-hosted
    steps:
      - uses: actions/checkout@v4
      - run: echo "hello from $(hostname)"

  test:
    runs-on:
      - self-hosted
      - "gharp-test-${{ github.run_id }}-${{ github.run_attempt }}"
    steps:
      - run: echo "tests from $(hostname)"

Every workflow_job whose runs-on set is fully covered by RUNNER_LABELS (default self-hosted; self-hosted is implicit on every self-hosted runner so you don't need to list it) or a configured dynamic label prefix (default gharp-) will get a fresh runner. Jobs requiring a label this pool doesn't advertise are dropped — see docs/configuration.md.

For the full deployment guide (from-source build, docker compose, volumes, upgrades, ops APIs, troubleshooting), see docs/deploy.md.

🤔 Why?

GitHub does not support "user-level" runners.

  • Runners are scoped to: repository, organization, or enterprise

This makes it hard to:

  • share a runner across multiple repositories
  • use self-hosted runners efficiently in personal accounts
  • scale runners dynamically

💡 This project solves that

  • Uses GitHub App + webhook (workflow_job)
  • Dynamically creates ephemeral runners per job
  • Automatically cleans up after execution

👉 You get GitHub-hosted-like behavior on your own machine

🏗️ Architecture

flowchart TB
    subgraph setup["One-time setup"]
        direction LR
        U[User] -- POST manifest --> GH1[GitHub]
        GH1 -- code + slug --> G1[gharp]
        G1 -- App credentials --> DB[(sqlite)]
        U -- install App --> GH1
    end

    subgraph runtime["Per-job runtime"]
        direction LR
        GH2[GitHub] -- workflow_job webhook --> G2[gharp]
        G2 -- record job --> DB2[(sqlite)]
        G2 -- registration token --> GH2
        G2 -- docker run --> R[ephemeral runner]
        R -- runs the job --> GH2
        R -- one job, then exit --> X((removed))
    end

    setup --> runtime
Loading

See docs/architecture.md for the full design, including the GitHub App Manifest flow, sqlite job durability, and permission scopes.

📦 Tech Stack

  • Go (server)
  • Docker (runner execution)
  • GitHub App (auth + webhook)

📄 License

Apache License 2.0 — Copyright 2026 Muhan Li.

🙌 Acknowledgements

About

gharp — Ephemeral, autoscaling self-hosted GitHub Actions runners for personal accounts. Multi-repo, Docker-only, no Kubernetes.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors