Deployment Best Practices
Twenty habits for shipping Rust binaries and containers that are small, reproducible, and observable.
Search across all documentation pages
Twenty habits for shipping Rust binaries and containers that are small, reproducible, and observable.
Always cargo build --release --locked in CI: Debug artifacts must never reach production.
Commit Cargo.lock for applications: Reproducible deploys depend on a frozen dependency graph.
Pin rust-version and CI toolchain: Match MSRV across dev laptops and pipelines.
Multi-stage Dockerfiles: Compile in builder; runtime image contains binary plus CA certs only.
Run as non-root in containers: Distroless nonroot or explicit USER directive.
Prefer rustls over OpenSSL in images: Fewer native deps and smaller cross-compile surface.
Expose /health and /ready: Liveness vs readiness must differ when DB is down.
Graceful shutdown on SIGTERM: Drain in-flight requests before exit (Tokio signal handlers).
Config via environment: Twelve-factor injection; no secrets in images or repos.
Structured JSON logs: tracing-subscriber JSON for centralized search.
RED metrics on HTTP services: Rate, errors, duration histograms exported to Prometheus.
Sample distributed traces: OTLP export at 1-10% unless debugging active incident.
Cross-compile in CI matrix: Build amd64 and arm64 before users report wrong arch binaries.
Strip or LTO release binaries: Smaller images and faster cold starts.
Run migrations as separate Job: Schema changes complete before new app pods serve traffic.
Tag releases with SemVer: Container tags and git tags align (v1.2.3).
Cache CI dependencies: Swatinem/rust-cache or sccache for sub-10-minute PR feedback.
Fail CI on clippy -D warnings and fmt --check: Style and lint debt blocks merge.
Document rollback: Previous image tag retained; kubectl rollout undo tested quarterly.
Post-deploy smoke test: Curl health endpoint and one critical API path from CI or synthetic monitor.
glibc on standard Linux distros; musl for scratch/Alpine when portability beats peak allocator performance.
Rolling is default on Kubernetes; blue/green when schema migrations need instant switchback.
Aim under 50 MB compressed for single Axum service; investigate if over 100 MB without ML deps.
Keep observability agents as platform sidecars; business logic stays one Rust binary per service.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Reviewed by Chris St. John·Last updated Jul 19, 2026