Skip to content

/flags/cargo/cargo-build-release

cargo build --release: optimised builds and target/release

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · cargo 1.98.1 (797e8a9bc 2026-08-05)updated
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

bash
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/app

Output

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

Settingdev (default)release (--release)
opt-level03
debug (debug info)truefalse
debug-assertionstruefalse
overflow-checkstruefalse
incrementaltruefalse
codegen-units25616
ltofalsefalse
Output foldertarget/debugtarget/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 --release helps when tests do heavy computation, at the cost of losing overflow checks during the run.

opt-level values

opt-levelMeaning
0No optimisation (dev default): fastest to compile, slowest code
1Basic optimisations
2Some optimisations
3All 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

Cargo.toml
[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 symbols

Custom profiles inherit from a built-in one and are selected with --profile NAME:

toml
[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/app is still the old debug build; run ./target/release/app or cargo run --release.
  • Profiles in dependencies are ignored. Cargo reads [profile.*] only from the workspace root Cargo.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.

Sources