Heavy builds on your own hardware

For builds that are too big or slow for a free GitHub-hosted runner.

Most Vega users should use GitHub-hosted CI with Vega's binary cache. That is the simplest setup and is documented in Caching your builds. Use this guide only when your Nix builds are too large, too slow, or too resource-intensive for GitHub's free hosted runners.

This runs a self-hosted GitHub Actions runner on your own hardware, for your own repository, and caches your builds to your own Vega tenant. It does not donate compute to Vega, does not run jobs for other users, and does not help corroborate the shared cache. A future, separate Vega feature may let you contribute compute to reproduce other users' published derivations; that is not what this configures.

The Vega builder is a reproducible OCI image (built with Nix dockerTools, cosign-signed, published to GHCR). The container is optional: if you already run Nix and a self-hosted GitHub Actions runner directly on the host, Vega can use that setup. The image is provided to give heavy builds a pinned, reproducible toolchain with host isolation, easy resource limits via --memory/--cpus, and clean teardown.

Pull and verify

Pull the image, resolve it to an immutable digest, verify that digest's signature, and run the digest, so what you run is exactly what you verified (a tag like :latest can move between verify and run):

docker pull ghcr.io/ad-astra-computing/vega-builder:latest
DIGEST=$(docker inspect --format '{{index .RepoDigests 0}}' \
  ghcr.io/ad-astra-computing/vega-builder:latest)
cosign verify "$DIGEST" \
  --certificate-identity-regexp '^https://github\.com/Ad-Astra-Computing/vega-agent/\.github/workflows/publish-builder-image\.yml@refs/tags/[^/]+$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Run it as a self-hosted runner

The image takes a short-lived registration token; no long-lived credential enters the container. Mint one with the GitHub CLI (it reuses your existing login), copy it into the container before starting it (so the token is never an env var visible to docker inspect and never readable by a job), and run the verified $DIGEST with hard resource caps so a build cannot exhaust the host:

TOKEN_FILE=$(mktemp)   # mktemp creates it mode 0600
gh api --method POST \
  repos/<owner>/<repo>/actions/runners/registration-token --jq .token > "$TOKEN_FILE"
chmod 0400 "$TOKEN_FILE"

cid=$(docker create --restart=unless-stopped --name vega-runner \
  --memory=12g --memory-swap=12g --cpus=4 \
  -e VEGA_MODE=runner \
  -e GITHUB_OWNER=<owner> \
  -e GITHUB_REPOSITORY=<repo> \
  -e GITHUB_RUNNER_TOKEN_FILE=/home/runner/.runner-token \
  -e GITHUB_RUNNER_LABELS=self-hosted,vega \
  "$DIGEST")
docker cp "$TOKEN_FILE" "$cid:/home/runner/.runner-token"
rm -f "$TOKEN_FILE"
docker start "$cid"

The --memory and --cpus caps are required: a large nix build will otherwise exhaust host RAM. --restart=unless-stopped brings the runner back after a crash or reboot; a restart reuses the saved registration and needs no new token, while only recreating the container (for example to update the image) re-registers. The runner only shows offline on GitHub while the container is actually down. You do not need to keep it online continuously: a runner that is offline simply receives no jobs, and Vega's network view counts builders that recently produced a reproduction, not idle uptime. Point your workflow at the runner with runs-on: [self-hosted, vega], and keep that workflow push-only (never let a fork's pull request target a self-hosted runner). The full runbook, including the sandbox options, is in the builder README.

What it does and does not earn

A self-hosted build caches to your own tenant namespace, signed with a per-tenant key. It is owner-controllable, so it never counts toward the distinct-owner agreement that promotes a build to the globally trusted shared cache. Where a build ran is provenance, not a trust signal. To publish builds without a self-hosted runner, see Caching your builds.