Skip to content

ARC Runner

arc-runner is the custom Actions Runner Controller image consumed by the arc-runner-set scale set in the arc-runners namespace. It layers a small bundle of build tooling on top of ghcr.io/actions/actions-runner so CI jobs do not have to apt-get install the same packages on every cold start.

Workflows that run on arc-runner-set currently install git-lfs, unzip, and similar tools as the first step of every job. The Unity build matrix in ci-unity.yml re-installs git-lfs once per build target — three installs per workflow run today, more as we add Unity targets. Cumulatively this adds minutes of wall-clock time to every Unity matrix and burns ARC pod CPU on apt repo round-trips that are identical across runs.

A baked image fixes the pattern in three ways:

  1. No cold-start apt-get — every tool the runner pool needs is on PATH the moment the runner pod starts.
  2. Air-gap-safe under apt mirror outages — image dependencies are pinned via the upstream actions-runner tag plus apt snapshots; a transient archive.ubuntu.com outage no longer fails Unity matrix legs.
  3. Single source of truth — bumping a tool happens once in this Dockerfile + version, not in every workflow that references it.
BinaryPurpose
actions-runnerUpstream GitHub Actions self-hosted runner (/home/runner/run.sh)
git-lfsForgejo LFS pulls (rareicon Unity, future chuck UE5)
unzipbutler installer + generic archive tooling
jqManifest dispatch + workflow shell glue
xz-utils.tar.xz extraction (Unity, butler, SDK installers)
ghGitHub CLI for workflows that shell out to it
curlInherited from upstream, kept explicit for downstream callers
ca-certificatesTLS trust for HTTPS-fetched dependencies
rsyncCross-host file sync (Unity / UE asset shuffling)
build-essential + pkg-configNative-build deps for crates that compile C/C++ on the runner (ci-uniti, ci-dashboard)
protobuf-compiler + libprotobuf-devprotoc for crates that codegen from .proto (used six times across ci-uniti)
gettext-base (envsubst)k8s manifest rendering (ci-dbmate-deploy)
postgresql-client (psql)Seed/init step in ci-dbmate-validate
kubectlPinned k8s client used by ci-dbmate-deploy; version controlled by KUBECTL_VERSION build arg
dbmatePinned migrator. ci-dbmate-validate runs it directly; ci-dbmate-deploy invokes it via the in-cluster Job (image bake = parity); version controlled by DBMATE_VERSION build arg

The image runs as the upstream runner user by default. The pod spec sets runAsUser: 0 and the entrypoint hands control to /home/runner/run.sh; a sudoers entry for root is baked in so a future cleanup can drop the inline command: override on arc-runner-set.

ArgDefaultPurpose
ACTIONS_RUNNER_VERSION2.334.0Upstream ghcr.io/actions/actions-runner tag the image is layered onto. Bump to follow upstream minor releases.
KUBECTL_VERSION1.31.0Pinned kubectl release. SHA verified against dl.k8s.io/release/v<KUBECTL_VERSION>/bin/linux/amd64/kubectl.sha256 at build.
DBMATE_VERSION2.28.0Pinned dbmate release. Tracks the in-cluster ci-dbmate-deploy Job image for parity.
  • apps/kube/github/runners/manifests/values.yamltemplate.spec.containers[].image and template.spec.initContainers[].image will flip to ghcr.io/kbve/arc-runner:<tag> in a follow-up PR. The first publish of this image happens on the upstream runner image; subsequent rebuilds run on the custom image itself.

This page tracks the image artefact only. The full migration runs across three PRs to keep rollback cheap:

  1. First publishghcr.io/kbve/arc-runner:0.1.1 via the standard ci-docker.yml dispatch path. The :latest tag is built locally; CI promotes the ci-{sha} build to :<mdx-version> at publish time, so the version in arc-runner.mdx frontmatter is the source of truth — project.json does not hardcode it.
  2. Follow-up — flip both image: references in arc-runner-set values.yaml and bump the kbve.com/restart-trigger annotation.
  3. Follow-up — strip the Install git-lfs step (and any other apt-get install band-aids) from ci-unity.yml once the new runner pods are confirmed serving jobs.
  • Track upstream actions/runner minor releases in ACTIONS_RUNNER_VERSION. Patch bumps can ride along on dependabot.
  • Every image publish bumps both version.toml (rebuild trigger via the dispatch manifest) and the kbve.com/restart-trigger annotation in values.yaml (forces a runner pod roll).