Statix

Build notes

Why another musl build environment?

Not because Rust's musl target is lacking, and not because muslrust or cross are insufficient. The real problem is narrower: once a Rust project starts depending on C/C++, CMake, pkg-config, OpenSSL, libpq, RocksDB, librdkafka, or bindgen/libclang, static musl build failures tend to occur at the boundary between the host toolchain and the target toolchain, not in Rust itself.

Summary

Statix should be understood as a conservative native-heavy musl build environment, not a general-purpose Rust CI image and not a replacement for cross. It trades a larger image, slower builds, and fewer targets for a more complete and reproducible x86_64-unknown-linux-musl native dependency path.

muslrust
Good for straightforward, lightweight static musl Rust binary builds, especially cloud containers and standalone Linux binaries.
cross
Good for multi-target cross build and cross test workflows where the abstraction is a Cargo wrapper over per-target container images.
Statix
Good for native-heavy x86_64 musl static builds where the C/C++ toolchain, static libraries, CMake, and LLVM/libclang all need to be locked together.

The problem is not “can it musl”

A pure Rust CLI usually does not need Statix. In many cases, rustup target add x86_64-unknown-linux-musl followed by a direct build is enough. Once a project introduces a native crate, the failure mode shifts to a different category:

Statix targets those boundaries. It does not wrap Cargo commands; it puts the Rust toolchain, musl GCC, static native libraries, CMake, LLVM/clang, libclang path, pkg-config defaults, and CA bundle defaults into one reproducible environment.

Differences from muslrust and cross

Dimension muslrust cross Statix
Core abstraction A Docker environment for building static musl Rust binaries. A cross build/test wrapper around cargo that selects container images per target. A complete build environment for native-heavy static musl binaries.
Target coverage Focused on common Linux musl static build paths. Covers a wide range of Linux, Android, BSD, Windows GNU, and embedded targets, with partial QEMU test support. Currently commits only to x86_64-unknown-linux-musl, getting one difficult path right first.
Native library strategy Ships a small set of common C libraries by default; additional libraries are usually added by downstream derived images. Default images cover the general cross toolchain; complex native libraries typically require a custom target image or configuration extension. Ships and configures zlib, libffi, ncurses, OpenSSL, curl, PostgreSQL/libpq, and other static dependencies by default.
CMake / libclang Oriented toward a directly usable static Rust build environment; full-musl CMake/libclang is not a core boundary. Can be addressed with a custom image, but that is not the primary value of cross itself. CMake and LLVM/libclang are intentionally source-built and placed on the musl-oriented toolchain path.
Validation scope Emphasizes producing distributable static binaries. Emphasizes whether a target can cross-compile non-trivial crates and supports testing on some targets. Emphasizes build, static check, Alpine runtime execution, and issue-backed native dependency fixtures.
Main cost When a native dependency is outside the default library set, downstream must extend the image. Complex native-heavy static musl projects may require maintaining a custom cross image. Larger image, slower builds, fewer targets; these costs are traded for less host/target toolchain mixing.

How to choose

Start with the Rust musl target directly
If the project is primarily pure Rust, or if native dependencies already have a well-established bundled or static path, the official target is usually the simplest option.
Consider muslrust for lightweight static containers
If the goal is to produce static Linux binaries quickly and native dependencies stay within its default boundaries, muslrust is a more mature and lighter choice.
Consider cross for multi-target workflows
If the core problem is building and testing the same Rust crate for many targets, the abstraction that cross provides is a better fit.
Consider Statix for native-heavy x86_64 musl
If failures are concentrated in CMake, libclang, pkg-config, OpenSSL, libpq, RocksDB, librdkafka, C++ runtime linkage, or static PIE linking, Statix is designed for that scenario.

The boundaries Statix tries to make explicit

The most difficult aspect of static musl builds is that there are too many paths that appear to compile. A build script may run on the host, a C library may come from a Debian package, a pkg-config file may point to a glibc path, or a Rust target flag may affect only the final link without touching the configure stage of a native dependency.

The design choice in Statix is to make those boundaries explicit: build the musl GCC toolchain first, then use it to build static C/C++ libraries and build tools; direct Cargo, the linker, pkg-config, OpenSSL, libpq, CMake, and libclang to the same musl-oriented path by default; then verify the result with file, ldd, and Alpine runtime execution.

This is not the minimal approach, but it is an explainable one. For small projects it may be too heavy; for native-heavy projects it eliminates the environmental assumptions that are difficult to express in a single cargo build --target ... line in a README.

Do not overstate it

Statix is still an early-stage tooling project. Its current advantage is a clear full-musl division of responsibility and reproducible validation, not ecosystem coverage, build speed, or image size.

References