incus-gh-runner

module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2026 License: Apache-2.0, MIT

README

incus-gh-runner

incus-gh-runner is a controller that runs ephemeral GitHub Actions runners in Incus virtual machines. It registers a runner scale set with GitHub, provisions a fresh VM with a just-in-time runner registration for each assigned job, and deletes the VM when its one job finishes.

Features

  • One job per VM: every job runs in a fresh virtual machine that is deleted afterward, so no state leaks between jobs.
  • Hot standby pool: a configurable minimum of connected idle runners absorbs bursts, bounded by a configurable maximum.
  • Cleanup-scoped reconciliation: the controller only counts and deletes VMs carrying its exact cleanup marker, and keeps no database of its own. The marker prevents accidental cross-controller cleanup; it is not an Incus authorization boundary.
  • Unattended operation: GitHub session recovery with capped backoff, bounded shutdown escalation, and a hardened systemd unit with credential isolation.
  • Bring your own image: any VM image that implements the documented guest contract works. The repository ships the guest-side components and a hardening guide for building one.

Requirements

  • A dedicated, single-purpose Linux host running Incus 7.0 or newer with QEMU VM support. Incus 6 is not supported.
  • Membership in the incus-admin group for the controller process. This is root-equivalent on the host. Do not deploy the current controller on an Incus host shared with unrelated trusted workloads.
  • A GitHub App or personal access token authorized to manage runner scale sets at the configured repository or organization.

Installation

Each GitHub release provides incus-gh-runner_<version>_<os>_<arch> binaries for Linux and macOS, installable DEB and RPM packages for Linux, and a multi-architecture controller OCI image. All support amd64 and arm64 and ship with checksums and build attestations.

Add the signed Meigma package repository and install with the host package manager:

sudo apt install incus-gh-runner
# or, on Fedora
sudo dnf install incus-gh-runner

Follow Deploy to production for copy-paste repository setup and full signing-key fingerprint verification. Packages install the binary, base systemd unit, tmpfiles policy, editable example configuration, and credential drop-in examples. They deliberately do not enable or start the service before host-specific configuration and credentials exist.

Versioned DEB and RPM files remain available from the releases page for direct or offline installation.

The raw binary remains available for manual installations:

install -m 0755 incus-gh-runner_<version>_linux_amd64 /usr/bin/incus-gh-runner

To build from source instead:

go build ./cmd/incus-gh-runner

See Build runner images for building the guest VM image the controller boots for each job.

Usage

The controller is a single foreground command driven by one configuration file:

incus-gh-runner --config config.yaml

The smallest working configuration:

github:
  config_url: https://github.com/OWNER/REPOSITORY
  scale_set: incus-gh-runner-example
  runner_group: default
  app:
    client_id: Iv1.xxxxxxxxxxxxxxxx
    installation_id: 12345678
    private_key_file: /path/to/private-key.pem
incus:
  project: github-runners
  image: incus-gh-runner-v1
  profiles: [github-runner]
  owner: incus-gh-runner-example

For a PAT, omit the app block and set github.token_file to a protected token file instead. Private-repository scope is the hardened starting point and pairs the scale set with only that repository. Public repositories require a separate threat review that prevents untrusted fork code from targeting the runner. Organization scope is supported only with a dedicated non-default runner group restricted to the selected repositories and commit-pinned workflows that may submit jobs. Set github.runner_group to that group's exact name. The controller rejects the default group for organization scope; enterprise URLs are outside the supported contract.

The referenced Incus project, image, and profiles must already exist; the controller creates the GitHub scale set automatically if it is absent. Jobs target the scale set by its name:

jobs:
  example:
    runs-on: incus-gh-runner-example

For production, run the controller under the hardened systemd unit in deploy/systemd/, selecting the GitHub App or PAT credential drop-in. Apply and validate the restricted project, network, profile, storage, resource limits, and controlled-egress baseline with the CUE policy and read-only drift tooling in deploy/incus/ first. Follow the deployment guide for the end-to-end path.

Documentation

Development

mise provides the locked toolchain and Moon is the task runner; CI runs the same aggregate gate through mise exec -- moon ci:

mise install
mise exec -- moon run root:check

Business logic stays isolated from the GitHub and Incus client adapters: third-party types live in internal/adapters, controller-owned ports live with the orchestration core, and scale-set callbacks only publish into a coalescing mailbox while Incus work runs in a bounded worker pool.

Unit tests run without Incus or GitHub access. Opt-in functional tests (INCUS_GH_RUNNER_TEST_* environment variables) exercise the real GitHub scale-set session and the destructive Incus VM lifecycle against explicitly disposable projects. See the comments in those test files for the required inputs.

Contributing and security

See CONTRIBUTING.md for the development workflow and SECURITY.md for private vulnerability reporting.

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Directories

Path Synopsis
cmd
incus-gh-runner command
deploy
incus
Package incuspolicy validates rendered Incus baselines against the embedded CUE policy.
Package incuspolicy validates rendered Incus baselines against the embedded CUE policy.
internal
adapters/github
Package github adapts the GitHub Actions runner scale-set client.
Package github adapts the GitHub Actions runner scale-set client.
adapters/incus
Package incus adapts the Incus client used to manage runner instances.
Package incus adapts the Incus client used to manage runner instances.
adapters/provenancefile
Package provenancefile loads bounded provenance inputs from the filesystem.
Package provenancefile loads bounded provenance inputs from the filesystem.
app
Package app composes controller ports and supervises their lifetimes.
Package app composes controller ports and supervises their lifetimes.
cli
config
Package config loads and validates immutable controller configuration.
Package config loads and validates immutable controller configuration.
controller
Package controller reconciles GitHub runner demand with owned runner capacity.
Package controller reconciles GitHub runner demand with owned runner capacity.
incusvalidate
Package incusvalidate compares a fail-closed Incus baseline with observed host state.
Package incusvalidate compares a fail-closed Incus baseline with observed host state.
projectinfo
Package projectinfo provides stable project metadata for user-facing adapters.
Package projectinfo provides stable project metadata for user-facing adapters.
provenance
Package provenance creates and verifies job-bound machine provenance receipts.
Package provenance creates and verifies job-bound machine provenance receipts.
runtime
Package runtime composes production adapters around the controller application.
Package runtime composes production adapters around the controller application.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL