/flags/cargo/cargo-build-release
cargo build --release: optimised builds and target/release
- tool
- Cargo
- command
- cargo build
- flag
- --release (-r)
- default
- off (dev profile, target/debug)
cargo build --release (short -r) builds with the release profile instead of the dev profile: optimisation level 3, no debug assertions, no overflow checks, and output in target/release/ instead of target/debug/. Use it for benchmarks and anything you ship; debug builds can be many times slower. The same flag works for cargo run, cargo test and cargo bench (which uses release settings by default).
Debug vs release
cargo new -q --vcs none app && cd app
cat > src/main.rs <<'EOF'
fn main() {
let x: u8 = std::env::args().count() as u8 + 254;
println!("debug_assertions: {}", cfg!(debug_assertions));
println!("254 + args = {}", x.wrapping_add(0));
let y = x + 1; // overflows when x is 255
println!("x + 1 = {y}");
}
EOF
cargo build -q && ./target/debug/app 2>&1 | grep -v "^note:" | sed -E 's/ \([0-9]+\) panicked/ panicked/; s#src.main#src/main#'; echo "debug exit: ${PIPESTATUS[0]}"
cargo build -q --release && ./target/release/appOutput
debug_assertions: true 254 + args = 255 thread 'main' panicked at src/main.rs:5:13: attempt to add with overflow debug exit: 101 debug_assertions: false 254 + args = 255 x + 1 = 0
In the dev profile the integer overflow panics; in the release profile overflow checks are off and the value wraps to 0. Never rely on wrapping: use wrapping_add or checked_add explicitly.
The two built-in profiles
| Setting | dev (default) | release (--release) |
|---|---|---|
| opt-level | 0 | 3 |
| debug (debug info) | true | false |
| debug-assertions | true | false |
| overflow-checks | true | false |
| incremental | true | false |
| codegen-units | 256 | 16 |
| lto | false | false |
| Output folder | target/debug | target/release |
When to use it
- Benchmarks and performance checks: always. Timing a debug build measures unoptimised code and can mislead by a large factor.
- Anything you ship: binaries for users, container images and deployment artifacts.
- Day-to-day development: usually not. Release builds take longer, and the dev profile keeps the checks (overflow, debug assertions) that catch bugs early.
- Slow tests:
cargo test --releasehelps when tests do heavy computation, at the cost of losing overflow checks during the run.
opt-level values
| opt-level | Meaning |
|---|---|
| 0 | No optimisation (dev default): fastest to compile, slowest code |
| 1 | Basic optimisations |
| 2 | Some optimisations |
| 3 | All optimisations (release default) |
| "s" | Optimise for binary size |
| "z" | Optimise for size and also turn off loop vectorisation |
Any setting can also be overridden for one run with an environment variable, for example CARGO_PROFILE_RELEASE_LTO=true cargo build --release, which is handy in CI.
Tune the release profile
[profile.release]
lto = "fat" # link-time optimisation across crates
codegen-units = 1 # slower build, often faster code
panic = "abort" # smaller binary, no unwinding
strip = true # remove symbolsCustom profiles inherit from a built-in one and are selected with --profile NAME:
[profile.dist]
inherits = "release"
lto = "fat"Pitfalls
- Behaviour differs between profiles. Integer overflow panics in debug and wraps in release, as the example shows, and
debug_assert!runs only in debug. A bug that appears in one build only often comes from here. - Running the wrong binary. After
cargo build --release,./target/debug/appis still the old debug build; run./target/release/apporcargo run --release. - Profiles in dependencies are ignored. Cargo reads
[profile.*]only from the workspace rootCargo.toml; settings in a member crate trigger a warning and have no effect.
FAQ
Where is the binary after cargo build --release?
In target/release/, named after the package (app, or app.exe on Windows). The debug build stays in target/debug/; the two are cached separately, so switching does not rebuild the other.
Is --release the same as --profile release?
Yes. --release is shorthand for --profile release, and -r is the short form of --release since Cargo 1.59.
How do I get debug symbols in a release build for profiling?
Set debug = true (or a lower level) under [profile.release] in Cargo.toml, or create a custom profile that inherits from release and turns debug info on, so normal release builds stay small.