rust-musl-cross, container images for static Rust binaries on uncommon targets
Docker images for compiling static Rust binaries using musl-cross
At a glance
- What is it?
- rust-cross/rust-musl-cross builds static Rust binaries against musl using musl-cross-make, with prebuilt images for nineteen targets including mips, s390x, riscv64, powerpc64 and the arm hard-float variants, plus Docker-convention alias tags so a multi-platform buildx Dockerfile can select the right image with TARGETARCH. The images come from a builder that does not cache libraries between builds, so they are meant for release builds.
- Who is it for?
- Adopt rust-musl-cross if you ship static Rust binaries for a target your CI does not have a toolchain for, especially mips, s390x, riscv64 or the arm hard-float variants. Skip it if your target is x86_64 or arm64, where the official Rust toolchain already produces static musl binaries without a container.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the images are for and how the target list is organised
rust-musl-cross is a set of Docker images for compiling static Rust binaries using musl-cross-make, inspired by rust-musl-builder.
The reason static musl binaries matter is distribution. A Rust binary linked against glibc carries a dependency on the system C library, and the version matters: build it on a newer distribution and it fails on an older one. A musl binary has no such dependency, which is what makes it the usual choice for Linux containers and for embedded devices with a different libc entirely.
The target list is the interesting part, because it goes well beyond the two architectures most people need. The stable toolchain images cover aarch64-unknown-linux-musl, arm-unknown-linux-musleabi, arm-unknown-linux-musleabihf, armv5te-unknown-linux-musleabi, armv7-unknown-linux-musleabi, armv7-unknown-linux-musleabihf, i586 and i686, loongarch64, powerpc64 and powerpc64le, riscv64gc and x86_64.
The nightly images cover the MIPS family, mips, mips64 for OpenWrt, mips64 with the labi64 ABI, mips64el, mipsel, and s390x.
That distribution of targets says what this project is for. A team shipping firmware to a router running OpenWrt needs a mips64-openwrt-linux-musl build. A team deploying to a mainframe or an s390x cluster needs s390x. A team building for a RISC-V board needs riscv64gc. None of those have a hosted CI image with the toolchain, and installing one by hand on every runner is the problem this solves.
The arm variants with the eabi and eabihf suffixes are the ones that trip people up. eabi is the older soft-float ABI and eabihf is the hard-float one, and picking the wrong one produces a binary that will not run on the target. The images exist for both so you do not have to build the toolchain yourself to find out.
The images are on Docker Hub under messense/rust-musl-cross and on GitHub Container Registry under ghcr.io/rust-cross/rust-musl-cross.
Why the target list is the interesting part
The nightly toolchain images for the MIPS family are worth a second look, because they are the clearest case for a project like this.
MIPS never had a hosted Rust toolchain in the usual sense, and the ABI variants make the problem worse. There is mips-unknown-linux-musl for the big-endian 32-bit target, mipsel for the little-endian one, mips64 for 64-bit big-endian, mipsel variants for little-endian 64-bit, mips64el for the 64-bit little-endian, and the labi64 ABI variants alongside the older ones. On top of that there is mips64-openwrt-linux-musl, which is a specific target for a specific router distribution.
Each of those is a separate toolchain to build, and a team shipping firmware to a MIPS router or a set-top box would otherwise have to build a cross toolchain on every build machine it has. Having the image means one docker pull and a cargo build, on a machine with nothing installed.
The same argument applies to s390x, which is the architecture of IBM mainframes, and to the armv5te and armv7 soft-float variants, which show up in embedded devices where the hardware has no floating point unit or where the ABI predates hard float.
The s390x and powerpc64 entries also point at a broader use: these are the architectures of the servers that large organisations still run, and a static musl binary is a common way to ship one piece of software across a mixed estate. The image removes the need for a build agent per architecture.
Using an image and the caching limitation
The usage is a pull and an alias. The README shows it for the armv7 hard-float target:
docker pull ghcr.io/rust-cross/rust-musl-cross:armv7-musleabihf
# Also available on Docker Hub
# docker pull messense/rust-musl-cross:armv7-musleabihfand then an alias so the build command stays short:
alias rust-musl-builder='docker run --rm -it -v "$(pwd)":/home/rust/src ghcr.io/rust-cross/rust-musl-cross:armv7-musleabihf'
rust-musl-builder cargo build --releaseThe volume mount is the mechanism. The current directory is mounted at /home/rust/src inside the container, so cargo sees the same source tree and writes the output binaries back to the host filesystem. The README notes that this assumes the directory is readable and writable by the container user.
The limitation is stated just after that, and it is the one thing to know before you use these images in a build pipeline. At the moment the images do not attempt to cache libraries between builds, so this is best reserved for making final release builds.
That is a significant caveat. A Rust project with a hundred dependencies takes a while to compile, and a container that recompiles everything from scratch on every invocation turns a two-minute build into a twenty-minute one. For a release pipeline that runs occasionally, that is an acceptable price for not having to maintain toolchains. For a development loop or a pull request build on every commit, it is not.
The reason is inherent to the approach. musl-cross-make builds a full cross toolchain, including the C library and the target's system headers, and that is a heavy operation. rust-musl-builder, the project this one is inspired by, solves the same problem with a different trade, and the ecosystem has more than one answer for it. What this one offers is the breadth of targets, particularly the ones nobody else builds.
The alias tags and how they fit into a multi-platform Dockerfile
The alias tags are the part of this project that makes it usable in a normal multi-platform build, and the idea is small and good.
Each primary tag has an alias that matches Docker's TARGETARCH build argument, and the format is TARGETARCH followed by musl. So aarch64-musl also answers to arm64-musl, x86_64-musl to amd64-musl, i686-musl to 386-musl, loongarch64-musl to loong64-musl, powerpc64-musl to ppc64-musl, powerpc64le-musl to ppc64le-musl, riscv64gc-musl to riscv64-musl.
The MIPS ones are interesting because the alias is not a direct match. mips64-muslabi64 answers to mips64-musl, mips64el-muslabi64 to mips64le-musl and mipsel-musl to mipsle-musl. Docker does not have a TARGETARCH value for those, so the alias is a convenience for people who want the shorter name rather than a mapping Docker relies on.
The payoff is a Dockerfile that needs no build arguments at all:
ARG TARGETARCH=amd64
FROM ghcr.io/rust-cross/rust-musl-cross:${TARGETARCH}-musl AS builderDocker sets TARGETARCH automatically from the native runner platform, so one docker buildx build --platform linux/amd64,linux/arm64 command selects the correct image on each architecture with nothing else configured.
That is the difference between this and a set of per-target build stages. A conventional multi-platform Dockerfile either has a FROM per architecture and duplicates the build steps, or it uses a single FROM with build arguments. The alias tags let the second work without you having to map Docker's architecture names onto the Rust target triples yourself, which is exactly the sort of mapping that goes wrong quietly and produces a binary for the wrong architecture.
The README notes one exception. The arm variants, arm-musleabi, arm-musleabihf, armv7-musleabi and armv5te-musleabi, are not aliased, because Docker's TARGETARCH is amd64 or arm64 and there is no corresponding value for the 32-bit ARM targets. Those images have to be referenced by their primary tags.
The project is MIT licensed, uses Bors for merge automation, has a Build workflow, and the maintainer asks for sponsorship to go full time on open source work. The last push was on 2026-10-01.
Editorial conclusion
Adopt rust-musl-cross if you ship static Rust binaries for a target your CI does not have a toolchain for, especially mips, s390x, riscv64 or the arm hard-float variants. Skip it if your target is x86_64 or arm64, where the official Rust toolchain already produces static musl binaries without a container. Verify first by building one binary inside the image and checking it with a static file command, since the point of the exercise is a binary with no dynamic dependencies.
Frequently asked questions
What do the rust-musl-cross images do?
They compile static Rust binaries against musl using musl-cross-make, so the resulting binary carries no dynamic dependency on the system C library. The images come from a builder that does not cache libraries between builds.
Which targets are covered by the prebuilt images?
Nineteen in total, including aarch64, several arm and armv5te and armv7 eabi and eabihf variants, i586 and i686, loongarch64, powerpc64 and powerpc64le, riscv64gc and x86_64 on the stable toolchain, and the mips family plus s390x on nightly.
How do I build with a rust-musl-cross image?
Pull the image for your target, then run it with the current directory mounted at /home/rust/src, for example docker run --rm -it -v "$(pwd)":/home/rust/src ghcr.io/rust-cross/rust-musl-cross:armv7-musleabihf, and then run cargo build --release inside it. The output binaries are written back to the host directory.
What are the Docker-convention alias tags for?
They let a multi-platform Dockerfile select the right image without extra build arguments, since the alias tag format is TARGETARCH followed by musl, so aarch64-musl answers to arm64-musl and x86_64-musl to amd64-musl. The 32-bit arm variants are not aliased because Docker has no TARGETARCH value for them.
Are these images suitable for a fast development loop?
The README says the images do not attempt to cache libraries between builds, so it is best reserved for making final release builds. A project with many dependencies will recompile from scratch on each invocation, which makes the images a poor fit for per-commit builds.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/rust-cross-rust-musl-cross)Was this useful?