Configuring a runner¶
A paired runner reads a saggar.toml: --config <path>, else
~/.config/saggar/saggar.toml ($XDG_CONFIG_HOME honored), else
/etc/saggar/saggar.toml. Precedence, increasing: built-in
defaults, the config file, SAGGAR_* environment variables, CLI
flags. Unknown keys anywhere are refused: a typo must never silently
disable the setting it was meant to set.
What you can configure on your runner:
data_dir = "~/.local/share/saggar"Root for everything the runner keeps on disk: build work trees (
work/), warm caches (cache/), cloned or uploaded sources (sources/), job logs (logs/), the artifact upload spool (blobs/) and the scratch root (scratch/). Env:SAGGAR_DATA_DIR; flag--data-dir.node_id = "<hostname>"The id the runner pairs and appears under. Default: the machine hostname. Env:
SAGGAR_NODE_ID.[envs]Environment resolution: fingerprint → OCI image ref.
[envs] "debian:12" = "debian:12" "debian:12 + rust" = "rust:1-bookworm"
Entries are optional: each plugin declares its own default image. It is the official one for its toolchain, rolling where the vendor publishes a rolling tag (
rust:1,node,python:3,ruby:3,golang:1,swift, all on the fingerprint’s own Debian release; full variants, never slim), or a fixed ref where it does not: uv pins its own distro, dotnet ships latest, composer and elixir stay pinned by hand and bumped when their line retires. For the JVM family the image derives from the project: gradle reads the toolchain level the build files declare, maven the compiler properties (never below 17). A table entry always wins over a plugin’s declaration. Pin images here for the toolchains no plain distro image carries: zig, snap (snapcraft-capable), flatpak (flatpak-builder-capable), rpm (a Fedora/CentOS image), appimage (FUSE + network). A fingerprint for another distro with no entry fails the job; there is no environment to run it in.[incremental]The warm caches (warm trees and env snapshots share the budget):
ttl_hours = 168: hours an entry may sit unused before eviction.max_total_gb = 20: total size budget; entries evict least-recently-used first.ccache = false: bind a persistent host compiler cache into image builds (opt-in; it only pays off on images that carry ccache).
Warm caches are runner-local. They speed up whatever lands on this machine, and a job may land on a cold one.
[network]Registry-mirror pinning, deliberately pull-side only: these keys route the runner’s own fetches. Arbitrary egress inside a build is not contained by them.
registry_mirror: docker-format base-image pulls go through this pull-through mirror (e.g.https://mirror.internal:5000fronting docker.io). The podman builder reads a job-scopedregistries.confpinningdocker.ioto it; thedockerdaemon builder cannot take a per-invocation registries config and ignores the pin (configure the mirror indaemon.json). Absent: pulls go to the image’s own registry. There is noapt_mirrorkey: the Debian builds bootstrap their chroot through the pkh library, which exposes no mirror option.
[atlas]url: the pkgatlas resolve instance; omit it and dependency resolution uses heuristics only. When set, missing-native-dependency evidence resolves through it (exact file→package answers for the image’s distro, release and arch).[plugins.debian]prune = false: sweep pkh’s residue after every Debian build. Only when Debian builds never overlap on the machine (the sweep cannot tell a live concurrent build’s chroot from crashed residue); the runner already sweeps once at startup.[plugins.docker]builder = "podman": daemonless rootless build (works inside an unprivileged container; needs the userns/uidmap/subuid prerequisites)."docker"opts into the host daemon: it needs the socket shared with the runner.cleanup_image = false: remove the job’ssaggar/<name>:<job-id>tag from the builder once the image is archived into the store. Off by default: the tag is part of the build’s output surface (a followingdocker runon the machine uses it). The build cache and base images are always kept.
What a runner ignores¶
The service-side sections (listen, token, [storage],
[projects], [quotas], [retention], [auth] and the
rest) mean nothing where builds run. A [storage] section on a
runner is ignored with a loud warning: every artifact byte streams
through the service under the job’s lease (see
The runner protocol), and store credentials do not belong on a build
machine.