Aprende Rust · 5 min
musl vs glibc: binarios estáticos y portables
Cómo compilar un binario de Rust totalmente estático con musl y cuándo conviene frente a glibc.
Todo programa de Rust en Linux usa la biblioteca C del sistema por debajo. Hay dos implementaciones habituales: glibc, la de la mayoría de distribuciones, y musl, una implementación más ligera. Cuál usas lo decide el target con el que compilas, y esa decisión cambia cómo se distribuye tu binario.
El target por defecto
En Linux, el target por defecto es x86_64-unknown-linux-gnu: usa glibc y enlaza de forma dinámica. Tu binario no lleva la biblioteca C dentro; la carga del sistema al arrancar. Es un target de Nivel 1 ("Tier 1"), el de máxima garantía de soporte.
El alternativo es x86_64-unknown-linux-musl: usa musl y permite un binario totalmente estático, sin dependencias de bibliotecas del sistema. Todo lo que el programa necesita viaja dentro del archivo. Es un target de Nivel 2.
Cómo compilar con musl
Primero agregas el target con rustup y luego compilas apuntando a él:
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
El resultado es un único archivo que puedes copiar a una imagen scratch o Alpine y ejecutar sin instalar nada más.
La cross-compilación puede complicarse cuando hay dependencias nativas en C. Una forma sencilla de evitar esa fricción es cargo-zigbuild, que usa el compilador de Zig como enlazador:
cargo install cargo-zigbuild
cargo zigbuild --release --target x86_64-unknown-linux-musl
glibc frente a musl
| Criterio | glibc (-gnu) | musl (-musl) |
|---|---|---|
| Enlazado | Dinámico | Estático (binario autocontenido) |
| Imagen de contenedor | Necesita base con glibc | Corre en scratch o Alpine, más pequeña |
| Portabilidad | Atada a la versión de glibc del sistema | Corre igual en cualquier distribución |
| Asignador de memoria | Rápido y maduro | Históricamente más lento (mitigable) |
| Compatibilidad con crates C | Máxima | Alguna crate nativa puede dar fricción |
| Resolución DNS/NSS | Soporte completo de NSS | Implementación propia, más limitada |
Dos matices merecen detalle. El asignador de memoria de musl ha sido históricamente más lento que el de glibc en cargas con mucha reserva y liberación de memoria; si lo notas, puedes mitigarlo usando otro asignador como mimalloc o jemalloc desde tu propio programa. Y la resolución de nombres: glibc usa NSS, que permite configuraciones del sistema que musl no implementa igual, lo que en algunos entornos cambia cómo se resuelven los hosts.*-musl enlazan de forma estática por defecto.
Cuándo usar cada uno
- musl para microservicios en contenedor y CLIs que vas a distribuir. El binario estático simplifica la imagen, viaja entre distribuciones sin sorpresas y reduce la superficie de la imagen final. Encaja con lo que vemos en el rendimiento de un microservicio en Rust.
- glibc por defecto para desarrollo local y cuando necesitas máxima compatibilidad o el mejor rendimiento del asignador. Es el camino con menos sorpresas si no tienes una razón para cambiar.
La regla práctica: desarrolla con glibc, distribuye con musl cuando el empaquetado lo justifique, y mide si el asignador te afecta antes de preocuparte por él.
Fuentes
- Platform Support - The rustc bookThe rustc book
- musl libcmusl
- Linkage - The Rust ReferenceThe Rust Reference
- GitHub - rust-cross/cargo-zigbuild: Compile Cargo project with zig as linkercargo-zigbuild