Skip to content

Runner Environment

Every ritual step runs on a fresh, ephemeral machine. This page documents exactly what that machine has on it, so you never have to find out by pushing.

In order of precedence:

  1. environment.base_image — an explicit image. Always wins.
  2. environment.language + environment.version — the default image for that language, at the version you asked for.
  3. Neither set → buildpack-deps:bookworm-curl.
environment:
language: go
version: "1.25" # → golang:1.25-bookworm

All Debian (“bookworm”) based. Debian, not Alpine: prebuilt vendor binaries, native node modules, and the cloud SDKs assume glibc, and some tools (notably gcloud) aren’t packaged for Alpine at all.

languageImageDefault version
gogolang:{version}-bookworm1.25
nodenode:{version}-bookworm22
pythonpython:{version}-bookworm3.12
rustrust:{version}-bookworm1
rubyruby:{version}-bookworm3
javaeclipse-temurin:{version}-jdk21
phpphp:{version}-cli-bookworm8.3
(unset / unknown)buildpack-deps:bookworm-curl

version must look like a version ([A-Za-z0-9._-]); anything else is ignored and the default is used, rather than producing a bogus image reference.

Each image ships that language’s toolchain, plus git, curl, wget, and a shell. It does not ship other languages. A Go image has no Node; a Node image has no Go. For a polyglot repo, add what you need with packages: (below) or bring your own image with base_image.

Installs OS packages on the machine before any step runs:

environment:
language: go
packages: [nodejs, gcloud]

Names map to what you actually need, not to a literal package name. Notably:

You writeWhat gets installedChecked via
nodejs / nodenodejs and npmnode
npmnodejs and npmnpm
python / python3python3, python3-pippython3
pip / pip3python3-pippip3
dockerdocker.iodocker
gcloudGoogle Cloud CLI (official tarball, symlinked onto PATH)gcloud
anything elsethat package, as-isthat binary

The nodejsnodejs + npm mapping matters: on Debian and Alpine, the nodejs package ships only the node binary. npm is a separate package. Asking for nodejs and getting sh: npm: not found is the single most common way to lose an afternoon here, so packages: [nodejs] now gives you both.

A package that can’t be installed fails the run immediately, with the reason in a synthetic install-packages step — rather than failing three steps later with a confusing command not found.

packages: installs on every run, on a machine that is thrown away afterwards — there is no dependency cache yet. If the install cost is hurting, pre-bake an image:

environment:
base_image: ghcr.io/my-org/ci:2026-07

Any image works, including slim ones: the machine installs curl itself if the image has neither curl nor wget.

The runner binary is not baked into the image. The machine boots your base image, downloads rituals-service from the data-plane (authenticated with the per-run scoped token), and execs it. Two consequences worth knowing:

  • The base image is entirely your choice — it never needs anything VeloGit-specific.
  • Runner fixes ship with a data-plane deploy; there’s no image for you to rebuild.
  • No dependency caching. Every run does a cold npm ci / go mod download, and packages: reinstalls from scratch. The workspace is a temp dir on a machine that is destroyed at exit. Pre-baking a base_image is the workaround today.
  • environment.version only selects the image tag. It doesn’t install a second toolchain into an image that lacks it.