Skip to content
Conduit

skill

incus-gha-runners

Several GitHub Actions self-hosted runners on one Linux host, each in its own Incus system container with its own Docker. Bash scripts plus an agent skill; switches for mainland China networks.

riverscn/incus-gha-runnerscommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/riverscn/incus-gha-runners itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 25, 2026

incus-gha-runners

简体中文

Several GitHub Actions self-hosted runners on one Linux host, each in its own Incus system container.

Using an AI coding agent? Install the skill:

npx skills add riverscn/incus-gha-runners

Then open the agent in your project and ask, for example:

Set up 4 GitHub Actions self-hosted runners for this repository on my host `ssh myhost`, each in its
own Incus container, using incus-gha-runners. Read the workflows to find the label and the system
packages the jobs need. Show me every change to the host before making it, and never print a token.

Later: "add two more runners", "e2e says a shared library is missing after the Playwright upgrade", "why is runner 3 offline?". The agent runs the scripts and asks before anything that changes the host or a runner.

Self-hosted runners that share one host as processes of one user get in each other's way: parallel jobs rewrite the same tool directories in $HOME, one job's docker compose down changes the network under another job's browser (net::ERR_NETWORK_CHANGED), and the docker group is root on the host. Here every runner gets an unprivileged Incus system container with its own systemd, dockerd, network namespace and runner user, copied from a template. Jobs see a machine of their own; workflows are written as for GitHub-hosted runners.

  • Bash scripts, run as root on the host: prepare-host, build-template, add-runner, remove-runner, apply-hooks, status, doctor. All idempotent, with --dry-run.
  • One configuration file per pool: repository or organisation, labels, names, how many.
  • Long-lived containers: caches, images and build cache stay between jobs. Not ephemeral runners.
  • Switches for mainland China: Debian mirror, a locally built base image instead of images.linuxcontainers.org, a Docker Hub pull-through cache.
  • Project-specific system dependencies (such as Playwright's browser libraries) as hook scripts.
  • Registration tokens are piped in from gh on your computer; the host needs no GitHub credentials.
  • An agent skill that runs the scripts and asks before anything that changes the host or a runner.

Quick start

On the host:

sudo git clone https://github.com/riverscn/incus-gha-runners /opt/incus-gha-runners
sudo install -d /etc/incus-gha-runners
sudo cp /opt/incus-gha-runners/config.example.env /etc/incus-gha-runners/config.env
sudoedit /etc/incus-gha-runners/config.env        # GITHUB_URL, RUNNER_LABELS, RUNNER_COUNT, NETWORK_PRESET
sudo /opt/incus-gha-runners/bin/prepare-host --dry-run   # read it, then run without --dry-run
sudo /opt/incus-gha-runners/bin/build-template

On your computer:

gh api -X POST repos/OWNER/REPO/actions/runners/registration-token --jq .token \
  | ssh HOST sudo /opt/incus-gha-runners/bin/add-runner --token-stdin --all
ssh HOST sudo /opt/incus-gha-runners/bin/doctor

Then use the label in a workflow: runs-on: [self-hosted, ci].

Documentation

  • Setup: from a bare host to working runners
  • Operations: more or fewer runners, a new template, hooks, migrating, several pools
  • Troubleshooting
  • Design: every configuration key, script, option and exit code

Tested on

Debian 13 (trixie) host, Docker 29.9, Incus 6.0.4 (Debian's package), as a QEMU virtual machine with 12 cores and 11G of memory, running 4 runners for a pnpm monorepo CI (type checks, Playwright e2e on desktop and mobile, Docker image builds with Compose), with NETWORK_PRESET=cn. The images.linuxcontainers.org path (BASE_IMAGE=images) is untested. Other hosts with systemd and Incus 6.0+ should work (the containers are Debian either way); see the host requirements.

Security

Jobs run the repository's code. The containers are unprivileged and cannot reach the host's files, the host's Docker or each other, but they can reach the host's network, and a container keeps its state between jobs. Use these runners only for repositories you trust, and do not let pull requests from forks run on them.

License

MIT

↑↓ navigate openesc close