A container image build tool for Rust applications, inspired by [`ko`](https://ko.build) for Go.
## Overview
krust builds container images for Rust applications without requiring Docker. It:
- Executes `cargo build` to compile your Rust application as a static binary using musl libc
- Packages the resulting binary into a minimal container image layer
- Pushes images to OCI-compliant registries by default (use `--no-push` to skip)
- Creates truly static binaries by default for maximum portability and security
## Quick Start
```bash
# Install krust
cargo install --path .
# Set up your repository
export KRUST_REPO=ttl.sh/$USER
# Build and run your Rust app as a container
docker run $(krust build)
```
## Installation
```bash
cargo install --path .
```
### Prerequisites
Install the Linux musl targets for static binary cross-compilation:
```bash
# For linux/amd64 (most common)
rustup target add x86_64-unknown-linux-musl
# For linux/arm64
rustup target add aarch64-unknown-linux-musl
# For linux/arm/v7
rustup target add armv7-unknown-linux-musleabihf
```
#### macOS Cross-compilation Setup
On macOS, you'll need a cross-compilation toolchain:
```bash
# Install musl cross-compilation tools
brew install filosottile/musl-cross/musl-cross
# Create a .cargo/config.toml in your project with:
cat > .cargo/config.toml << 'EOF'
[target.x86_64-unknown-linux-musl]
linker = "x86_64-linux-musl-gcc"
[target.aarch64-unknown-linux-musl]
linker = "aarch64-linux-musl-gcc"
EOF
```
Note: krust builds fully static binaries by default using musl libc, ensuring maximum portability across different Linux distributions and container environments.
## Usage
krust outputs the pushed image reference by digest to stdout, with all other output going to stderr. This enables composability with other tools.
### Build a project in the current directory
```bash
# Set your repository prefix
export KRUST_REPO=ttl.sh/jason
# Build and push (default behavior)
krust build
# Build without pushing
krust build --no-push
# Build, push, and run immediately
docker run $(krust build)
```
### Build a specific directory
```bash
# Build and push a specific project
krust build path/to/rust/project
# Build without pushing
krust build example/hello-krust --no-push
```
### Override the image name
```bash
# Use a specific image name (overrides KRUST_REPO)
When you don't specify `--platform`, krust automatically detects which platforms to build for by inspecting the base image:
```bash
# If using cgr.dev/chainguard/static:latest (supports linux/amd64 and linux/arm64)
krust build # Automatically builds for both amd64 and arm64
# If using a single-platform base image
krust build # Builds only for the supported platform
# You can always override with explicit platforms
krust build --platform linux/amd64 # Build only for amd64 regardless of base image
```
This intelligent platform detection ensures your images support the same platforms as your base image, maintaining consistency throughout your image stack.
krust builds fully static binaries by default using:
- musl libc for Linux targets
-`RUSTFLAGS="-C target-feature=+crt-static"` for static linking
- Distroless static base image (`gcr.io/distroless/static:nonroot`)
This ensures your applications work across all Linux distributions without dependency issues.
### Why musl instead of glibc?
krust uses musl libc instead of glibc for several important reasons:
1.**True static linking** - musl is designed for static linking, while glibc uses dynamic loading internally (NSS) that breaks in static binaries
2.**Smaller binaries** - musl static binaries are typically 5-10x smaller than glibc equivalents
3.**No runtime surprises** - glibc static binaries often fail at runtime with DNS resolution, user lookups, or locale issues
4.**Container-optimized** - musl's simplicity makes it ideal for containers where you want minimal dependencies
5.**Security** - Smaller attack surface with fewer moving parts
The tradeoff is that musl has slightly different behavior than glibc in some edge cases, but for most applications this is not an issue. If your application requires glibc-specific behavior, you can override the default by building locally with cargo and creating your own container image.
## Environment Variables
-`KRUST_REPO` - Default repository prefix for built images (e.g., `ttl.sh/username`)
-`KRUST_IMAGE` - Override the full image reference for a build