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:
- Build scripts resolve host glibc tools instead of musl target tools.
pkg-configresolves host libraries; the problem surfaces only at link time.- CMake starts but uses the wrong compiler, sysroot, or linker flags.
bindgenrequires libclang, which comes from the host distribution path.- Libraries like OpenSSL, libpq, RocksDB, and librdkafka require a static build, correct link order, and runtime verification.
- A successful compilation does not mean the binary actually runs in Alpine or a scratch/distroless environment.
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,
muslrustis 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
crossprovides 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.
- It does not replace the multi-target capability of
cross. - It should not be used as a minimal runtime image.
- It cannot guarantee that every upstream project is musl-compatible without patches.
- Its default path is conservative, so build cost is higher than lightweight images.
- Validation should continue to rely on small fixtures and issue-backed native dependency cases, not a single large workload.