Deployment Best Practices
Twenty habits for shipping Rust binaries and containers that are small, reproducible, and observable.
-
Always
cargo build --release --lockedin CI: Debug artifacts must never reach production. -
Commit
Cargo.lockfor applications: Reproducible deploys depend on a frozen dependency graph. -
Pin
rust-versionand 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
nonrootor explicitUSERdirective. -
Prefer rustls over OpenSSL in images: Fewer native deps and smaller cross-compile surface.
-
Expose
/healthand/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-subscriberJSON 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-cacheor sccache for sub-10-minute PR feedback. -
Fail CI on
clippy -D warningsandfmt --check: Style and lint debt blocks merge. -
Document rollback: Previous image tag retained;
kubectl rollout undotested quarterly. -
Post-deploy smoke test: Curl health endpoint and one critical API path from CI or synthetic monitor.
FAQs
musl or glibc in prod?
glibc on standard Linux distros; musl for scratch/Alpine when portability beats peak allocator performance.
Blue/green vs rolling?
Rolling is default on Kubernetes; blue/green when schema migrations need instant switchback.
How small should images be?
Aim under 50 MB compressed for single Axum service; investigate if over 100 MB without ML deps.
Single binary or sidecar?
Keep observability agents as platform sidecars; business logic stays one Rust binary per service.
Related
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+.