Files
pixi-build-rust/README.md
T
2026-08-07 13:39:56 -04:00

3.5 KiB

pixi-build-rust

The container image the Gitea Actions runner uses to build Rust projects with pixi.

What it is

A push to a Rust project runs its CI, nightly, and release workflows on a runner, and every one of those jobs needs the same Rust toolchain. This image is the environment those jobs run in.

It is deliberately not small. The runner host has one vCPU and a gigabyte of memory, so the slow parts of a build are downloading the toolchain and compiling from scratch. This image front-loads both: the compiler is already in pixi's package cache before any job starts, and sccache is present so unchanged dependencies come from a cache instead of a rebuild. A job that would otherwise spend minutes fetching rustc and gcc spends none.

Node runs the JavaScript actions Gitea uses to bootstrap a job, such as actions/checkout. git performs the clone, commit, and push, ca-certificates lets it speak HTTPS, and curl is what the release workflow uses to call the Gitea API. The compiler is not installed through the system package manager; it comes from pixi, along with a C compiler and pkg-config. No credentials live in the image: the workflows authenticate with the built-in job token Gitea gives each run.

The toolchain is baked by pointing PIXI_CACHE_DIR at a fixed path and installing a throwaway environment at build time that pins the same rust version the projects use. pixi stores the downloaded packages there, and a job's pixi install then hardlinks them into place without a download. Keep the RUST_VERSION build argument in step with the projects' pixi.toml, and rebuild when it moves.

Build

Build the image on your own machine and push it to Gitea's container registry. The runner only ever pulls it.

You will need an access token with package:write access. Authenticate Docker with it, then build for linux/amd64, since that is what the runner host is. Architecture matters more here than for a lighter image: the toolchain pixi installs is built for glibc on amd64, so an arm64 image will push but fail to start with an exec-format error.

docker login git.scient.ing

docker buildx build --platform linux/amd64 \
  -t git.scient.ing/infra/pixi-build-rust:1 --push .

Bump the tag with a revision suffix (:2, :3) whenever the Dockerfile changes, including when you move the pinned rust or sccache version. The image is always pulled by an explicit version, never latest, so a runner's behavior stays tied to a named artifact you can roll back to.

Use

A runner advertises a label that maps to this image, and a project's workflows select it with runs-on.

# in the runner's config.yaml
runner:
  labels:
    - "pixi-build-rust:docker://git.scient.ing/infra/pixi-build-rust:1"
# in each Rust repository's .gitea/workflows/*.yml
jobs:
  build:
    runs-on: pixi-build-rust

The baked cache makes the first job on a fresh container fast, but job containers are removed when they finish, so anything a job writes is gone by the next run. To keep the crate registry and the compile cache between runs, mount them as named volumes in the runner's config.yaml.

# in the runner's config.yaml
container:
  options: >-
    -v pixi-cache:/opt/pixi/cache
    -v cargo-home:/opt/cargo
    -v sccache:/opt/sccache

Docker fills an empty named volume from the image's contents at that path the first time it is used, so the baked toolchain is present on the first job and the volume holds any later changes. That is runner configuration, not part of this image.