Profiles & Build Settings
Cargo profiles control optimization, debug info, panic strategy, and link-time optimization. Tuning them is how you trade compile time for runtime speed and binary size.
Search across all documentation pages
Cargo profiles control optimization, debug info, panic strategy, and link-time optimization. Tuning them is how you trade compile time for runtime speed and binary size.
[profile.dev]
opt-level = 0
debug = true
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = "symbols"When to reach for this: Shipping production binaries, CI release builds, or shrinking deploy artifacts.
Release profile for an Axum API service:
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
panic = "abort"
strip = "symbols"
[profile.release-fast]
inherits = "release"
lto = false
codegen-units = 16cargo build --release
cargo build --profile release-fastWhat this demonstrates:
opt-level = 3 maximizes runtime performancecodegen-units = 1 improves optimization at cost of compile timerelease or dev| Key | Dev typical | Release typical |
|---|---|---|
opt-level | 0 | 3 or s for size |
debug | true | false or limited |
lto | false | thin or fat |
codegen-units | 256 | 1 for max perf |
panic | unwind | abort for smaller binaries |
strip | none | symbols or debuginfo |
[profile.release-size]
inherits = "release"
opt-level = "z"
lto = true
panic = "abort"
strip = "symbols"opt-level = "s" or "z" favors smaller binariescargo bloat to find large symbolsRUSTFLAGS and Target CPURUSTFLAGS="-C target-cpu=native" cargo build --releasetarget-cpu=native uses host CPU features (not portable).cargo/config.toml for team-wide defaults--release always.thin in CI, fat only for final release artifacts.panic = "abort" without testing - unwinding-based Drop cleanup assumptions break. Fix: verify FFI and scoped guards still behave correctly.inherits must name an existing profile. Fix: cargo build --profile foo locally before CI.strip = "debuginfo" only.| Alternative | Use When | Don't Use When |
|---|---|---|
opt-level = 2 | Faster compile, near-release perf | Last-mile latency tuning |
cargo bloat --release | Find size regressions | Proving algorithmic improvements |
sccache / mold | CI compile/link speed | Replacing sound profile choices |
Thin LTO optimizes across crates with faster links. Fat LTO is more aggressive and slower, often yielding smaller or faster binaries.
For final release binaries where compile time is acceptable, yes. For dev iteration, keep defaults.
Use cargo check, split workspace crates, disable unused default features, and consider opt-level = 1 in a custom dev-fast profile.
Removes symbol tables from the binary, reducing size. Keep separate debug info if you need symbolicated crash reports.
[profile.release.package.foo] overrides settings per dependency crate (e.g. always optimize a hot path dep).
Result still works; only panics abort the process instead of unwinding. catch_unwind no longer catches panics.
Enabled in dev by default. Release builds typically disable it for deterministic optimized output.
Use Criterion with the same inputs across profiles; compare wall time and binary size with ls -la target/release/foo.
LTO can inline across crate boundaries, exposing optimizations but also longer link steps and higher memory use during build.
.cargo/config.toml under [build] rustflags = [...] applies to all workspace members unless overridden.
cargo bloatStack 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 16, 2026