Nix CI Docker images
This directory builds the Docker images used by xrpld's Linux CI. Each image
bundles the exact same toolchain that the Nix development shell provides
(see docs/build/nix.md), so what runs in CI matches
what developers get locally from nix develop.
The toolchain (CMake, Ninja, Conan, GCC, Clang, clang-tidy, the
sanitizer/coverage tools, …) is defined in nix/packages.nix
and assembled for CI by nix/ci-env.nix. The Docker build
turns that Nix environment into an ordinary container image layered on top of a
conventional base image (Ubuntu, Debian, RHEL, or nixos/nix).
Images
The images are built by the build-nix-images.yml
workflow and pushed to ghcr.io/xrplf/xrpld/nix-<distro>. The <distro> is
selected through the BASE_IMAGE build argument; the base images are the
oldest supported version of each distribution we target:
| Image | BASE_IMAGE |
Notes |
|---|---|---|
nix-nixos |
nixos/nix:latest |
Build/lint only; binaries are not run (see below). |
nix-ubuntu |
ubuntu:20.04 |
Oldest supported Ubuntu (glibc 2.31). |
nix-debian |
debian:bookworm |
|
nix-rhel |
registry.access.redhat.com/ubi9/ubi:latest |
All images carry the full toolchain on PATH (via /nix/ci-env/bin) plus the
CA bundle shipped in the Nix environment, so HTTPS clients (git, curl, Conan)
work without ca-certificates being installed in the base image.
Build stages
Dockerfile is a multi-stage build:
builder— On anixos/nixbuilder, evaluate the flake and build the CI environment (nix/ci-env.nix). The resulting Nix store closure (the complete set of store paths the toolchain depends on) is copied into a staging directory.final— Start fromBASE_IMAGE, copy in the Nix store closure and theci-envsymlink tree, and wire upPATHand the CA bundle. It then:- installs the dynamic linker if the base image lacks one (see How libc is handled),
- runs
bin/check-tools.shto verify every expected tool is present and runnable, and - compiles the C++ test programs in
test_files/with bothg++andclang++, and sanitizers.
tester— Start again from a cleanBASE_IMAGE(no Nix toolchain), install only the sanitizer runtime libraries (install-sanitizer-libs.sh), and run the binaries compiled infinal. This proves the binaries built with the Nix toolchain actually run on a vanilla base image. Onnixos/nixthis step is skipped (the binaries are patched for a conventional FHS loader).- Output — The final image is gated on the tester succeeding: it copies a
sentinel file out of
tester, so a failed test run fails the whole build.
How libc is handled
The goal is for binaries built in these images to run on the oldest supported base image (Ubuntu 20.04, glibc 2.31) and newer — without the developer's Nix toolchain being present at runtime. Two pieces make that work:
-
Compilers linked against an old glibc. The Nix CI environment does not use nixpkgs' current glibc. Instead it pins a 2020 nixpkgs snapshot whose primary glibc is 2.31 (matching Ubuntu 20.04), via the
nixpkgs-custom-glibcflake input. GCC, Clang, binutils and compiler-rt are all rebuilt/wrapped against this custom glibc (seenix/ci-env.nix). As a result the libraries they emit (libstdc++,libgcc_s, the sanitizer runtimes) reference only symbols available in glibc 2.31. -
An expected dynamic linker in the image. Binaries built in Nix environments reference a dynamic linker from Nix store paths, which won't be present in the base image. However,
loader-path.shreports the expected loader path for the current architecture, so we can patch the binaries to use the correct loader.
The build then verifies all of this end to end: the test programs in
test_files/ (a regular binary plus ASan/TSan/UBSan variants) are compiled in
final, their PT_INTERP is patched to the target loader, and they are run in
the clean tester stage to confirm each emits the expected sanitizer
diagnostic on a stock base image.
Files
| File | Purpose |
|---|---|
./Dockerfile |
Multi-stage build described above. |
./loader-path.sh |
Print the dynamic-linker (PT_INTERP) path for the current architecture. |
./test_files/ |
C++ sources and scripts to compile and run the sanitizer smoke tests. |
/bin/check-tools.sh |
Verify every expected tools are present and runnable. |
/bin/install-sanitizer-libs.sh |
Install libasan/libtsan/libubsan runtimes on the supported base images. |