Skip to content

Runner Pool

ImpVMRunnerPool manages ImpVM instances for CI/CD jobs. It currently supports GitHub Actions self-hosted runners.

Sections

How it Works

The pool controller creates and removes ImpVM objects to match runner demand. Each VM receives one JIT registration setup, then runs as an ephemeral GitHub Actions self-hosted runner. The controller removes terminal VMs and reconciles replacement capacity.

For GitHub Actions, Imp uses a GitHub App stored in a Kubernetes Secret. The operator mints one short-lived registration configuration for each runner VM. It passes that configuration directly to the guest runner at launch time. The configuration must not be placed in the VM manifest, the base image, or logs.

Runner image contract

The recommended v0.10.1 image layout keeps the operating-system base separate from the CI runner layer:

  • Base image: Debian-slim with glibc, the runner user (uid 10001), certificate and .NET runtime libraries, and CMD ["/usr/local/bin/runner"].
  • Runner layer: the GitHub Actions runner under /home/runner/actions-runner plus /usr/local/bin/runner. Imp supplies the one-time JIT value as IMP_GITHUB_JITCONFIG; the wrapper exports it as ACTIONS_RUNNER_INPUT_JITCONFIG, sets RUNNER_ALLOW_RUNASROOT=1, and executes run.sh for one job. The wrapper does not run config.sh --jitconfig itself.

Published images are built for Linux amd64 by syscode-labs/imp-runner-images. Use immutable digests in a template; do not use a moving tag in a production pool. Resolve and verify the digest from the intended publication before copying it into a pool—this guide deliberately does not assert an unverified version-to-image mapping. An image publication is evidence that a layer was built and pushed, not proof that a runner has registered or completed a job in your cluster.

GitHub Actions Visibility

GitHub only sees capacity after runners register. The pool keeps minIdle pre-registered runners available for fast job pickup. Monitor both the Kubernetes side (pool status, ImpVM count) and the GitHub side (runner inventory).

Minimal Example

yaml
apiVersion: imp.dev/v1alpha1
kind: ImpVMClass
metadata:
  name: runner-class
  namespace: default
spec:
  vcpu: 2
  memoryMiB: 2048
  diskGiB: 10
---
apiVersion: imp.dev/v1alpha1
kind: ImpVMTemplate
metadata:
  name: gh-runner
  namespace: default
spec:
  classRef:
    name: runner-class
  image: <immutable-runner-base-image>
  runnerLayer: <immutable-runner-layer-image>
---
apiVersion: imp.dev/v1alpha1
kind: ImpVMRunnerPool
metadata:
  name: ci-pool
  namespace: default
spec:
  templateName: gh-runner
  labels: [imp]
  platform:
    type: github-actions
    scope:
      org: my-org
    runnerGroup: omni-runner
    credentialsSecret: github-runner-app
    tokenSource: github_app
  scaling:
    mode: polling
    minIdle: 1
    maxConcurrent: 5
    scaleUpStep: 1
    cooldownSeconds: 30
    polling:
      enabled: true
      intervalSeconds: 30

Create the github-runner-app Secret before applying this pool. The E2E runbook shows its required fields. The runner layer must provide /usr/local/bin/runner, and both images should use independently verified immutable digests.