Deployment Basics
Rust compiles to native binaries with no runtime VM. That single artifact is the core deployment advantage: copy one file, set env vars, run under systemd or Kubernetes.
Recipe
# Release build (optimized, stripped optional)
cargo build --release
./target/release/my-service
# Verify linked libraries (glibc vs static)
ldd target/release/my-service 2>/dev/null || file target/release/my-service# Minimal env for a 12-factor service
export RUST_LOG=info
export PORT=8080
export DATABASE_URL=postgres://...
./target/release/my-serviceWhen to reach for this:
- Shipping APIs, workers, and CLIs to Linux servers
- Replacing interpreted services where cold start and memory matter
- Edge or container deployments needing small images
Working Example
# Project layout
# my-service/
# Cargo.toml
# src/main.rs
cargo build --release
install -m 755 target/release/my-service /usr/local/bin/my-service
# systemd unit excerpt
# ExecStart=/usr/local/bin/my-service
# EnvironmentFile=/etc/my-service/env// src/main.rs - bind port from env
use std::env;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let port: u16 = env::var("PORT")?.parse()?;
tracing::info!(%port, "starting");
// axum::serve(...)
Ok(())
}What this demonstrates:
- One binary per binary crate in
Cargo.toml - Config from environment at startup
- Structured logs via
tracingfor production
Deep Dive
Deployment Shapes
| Shape | Pros | Notes |
|---|---|---|
| Bare metal / VM | Simple ops | Use systemd restart policies |
| Container | Reproducible | Multi-stage Dockerfile |
| Kubernetes | Scale-out | Liveness/readiness probes |
| Static CLI | No server | Cross-compile for targets |
Health Endpoints
Expose /health (process up) and /ready (DB reachable) before traffic shifts in load balancers.
Gotchas
- Debug build in prod - 10-100x slower. Fix: always
--releasewith profile tuned inCargo.toml. - Missing
RUST_BACKTRACEpolicy - noisy logs vs debuggability. Fix: off by default, enable per incident. - Config files baked into image - environment drift. Fix: inject env or mounted secrets.
- No graceful shutdown - dropped connections on deploy. Fix:
tokio::signaland drain (see Tokio graceful shutdown). - Huge debug symbols in image - slow pulls. Fix:
stripor split debug info in CI.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
cargo install | Dev tools | Immutable infra |
| Nix / Flox | Reproducible dev+prod | Team has no Nix expertise |
| WASM on edge | Browser/Workers | Full POSIX server |
FAQs
Do I need Rust installed on the server?
No for release binaries. Only the compiled artifact and libc (unless fully static).
How do I run migrations?
Separate migrate binary or subcommand; run as Kubernetes Job before rollout.
One repo, many binaries?
Define multiple [[bin]] targets or workspace members; deploy each artifact separately.
What about Windows servers?
Cross-compile with x86_64-pc-windows-msvc target; ship as service or MSI via packaging tools.
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+.