As a backend project, johnthagen/min-sized-rust has picked up 9.9k stars on GitHub (Rust). π¦ How to minimize Rust binary size π¦ https://github.com/johnthagen/min-sized-rust
Snapshot summary built from the project's own GitHub metadata β there's no written TopGit review yet. The page will update automatically when a full review is published.
WHY NO REVIEW YET
TopGit writes full reviews for the most-starred, most-requested repositories. This page is a snapshot until then β see the READ ME tab for the original README in full.
This repository demonstrates how to minimize the size of a Rust binary.
By default, Rust optimizes for execution speed, compilation speed, and ease of debugging
rather than binary size, since for the vast majority of applications this is ideal. But
for situations where a developer wants to optimize for binary size instead, Rust provides
mechanisms to accomplish this.
Build in Release Mode
By default, cargo build builds the Rust binary in debug mode. Debug mode disables many
optimizations, which helps debuggers (and IDEs that run them) provide a better debugging
experience. Debug binaries can be 30% or more larger than release binaries.
To minimize binary size, build in release mode:
$ cargo build --release
strip Symbols from Binary
By default on Linux and macOS, symbol information is included in the compiled .elf file. This
information is not needed to properly execute the binary.
Cargo can be configured to
automatically strip binaries.
Modify Cargo.toml in this way:
[profile.release]
strip = true # Automatically strip symbols from the binary.
Prior to Rust 1.59, run strip directly on
the .elf file instead:
$ strip target/release/min-sized-rust
Optimize For Size
Cargo defaults its optimization level to 3 for release builds,
which optimizes the binary for speed. To instruct Cargo to optimize for minimal binary
size, use the z optimization level in
Cargo.toml:
[profile.release]
opt-level = "z" # Optimize for size.
[!NOTE]
In some cases the "s" level may result in a smaller binary than "z", as explained in the
opt-level documentation:
It is recommended to experiment with different levels to find the right balance for your project.
There may be surprising results, such as ... the "s" and "z" levels not being necessarily
smaller.
Enable Link Time Optimization (LTO)
By default,
Cargo instructs compilation units to be compiled and optimized in isolation.
LTO instructs the linker to optimize at the
link stage. This can, for example, remove dead code and often times reduces binary size.
Enable LTO in Cargo.toml:
[profile.release]
lto = true
Dynamic Linking: Why It Doesn't Work
Some might suggest using prefer-dynamic for smaller binaries, but this approach has critical limitations:
No stable ABI - binaries break between Rust versions
Community consensus - static linking preferred for reliability
Reduce Parallel Code Generation Units to Increase Optimization
By default, Cargo specifies 16 parallel codegen units for release builds.
This improves compile times, but prevents some optimizations.
Set this to 1 in Cargo.toml to allow for maximum size reduction optimizations:
[profile.release]
codegen-units = 1
Abort on Panic
[!IMPORTANT]
Up to this point, the features discussed to reduce binary size did not have an
impact on the behaviour of the program (only its execution speed). This feature does
have an impact on behavior.
By default, when Rust code encounters a situation when it must call panic!(),
it unwinds the stack and produces a helpful backtrace. The unwinding code, however, does require
extra binary size. rustc can be instructed to abort immediately rather than unwind, which
removes the need for this extra unwinding code.
Enable this in Cargo.toml:
[profile.release]
panic = "abort"
Remove Location Details
By default, Rust includes file, line, and column information for panic!() and [track_caller]
to provide more useful traceback information. This information requires space in the binary and
thus increases the size of the compiled binaries.
To remove this file, line, and column information, use the unstable
rustc-Zlocation-detail
flag:
With the
-Zfmt-debug flag
you can turn #[derive(Debug)]and
{:?} formatting into no-ops. This
will ruin output of dbg!(), assert!(), unwrap(), etc., and may break code that unwisely
relies on the debug formatting, but it will remove derived fmt functions and their strings.
[!NOTE]
See also Xargo, the predecessor to build-std.
Xargo is currently in maintenance status.
[!NOTE]
Example project is located in the build_std folder.
Rust ships pre-built copies of the standard library (libstd) with its toolchains. This means
that developers don't need to build libstd every time they build their applications. libstd
is statically linked into the binary instead.
While this is very convenient there are several drawbacks if a developer is trying to
aggressively optimize for size.
The prebuilt libstd is optimized for speed, not size.
It's not possible to remove portions of libstd that are not used in a particular application
(e.g. LTO and panic behaviour).
This is where build-std
comes in. The build-std feature is able to compile libstd with your application from the
source. It does this with the rust-src component that rustup conveniently provides.
Install the appropriate toolchain and the rust-src component:
# Find your host's target triple.
$ rustc -vV
...
host: x86_64-apple-darwin
# Use that target triple when building with build-std.
# Add the =std,panic_abort to the option to make panic = "abort" Cargo.toml option work.
# See: https://github.com/rust-lang/wg-cargo-std-aware/issues/56
$ RUSTFLAGS="-Zlocation-detail=none -Zfmt-debug=none" cargo +nightly build \
-Z build-std=std,panic_abort \
-Z build-std-features="optimize_for_size" \
--target x86_64-apple-darwin --release
The optimize_for_size flag provides a hint to libstd that it should try to use algorithms
optimized for binary size. More information about it can be found in the
tracking issue.
On macOS, the final stripped binary size is reduced to 51KB.
Remove panic String Formatting with panic=immediate-abort
Even if panic = "abort" is specified in Cargo.toml, rustc will still include panic strings
and formatting code in final binary by default.
An unstable panic=immediate-abort feature
has been merged into the nightlyrustc compiler to address this.
To use this, repeat the instructions above to use build-std, but also pass
-Zunstable-options -Cpanic=immediate-abort and
-Z build-std-features= (which will disable the default backtrace and panic-unwind features)
to rustc.
On macOS, the final stripped binary size is reduced to 30KB.
Remove core::fmt with #![no_main] and Careful Usage of libstd
[!NOTE]
Example projects are located in the no_main folder.
Up until this point, we haven't restricted what utilities we used from libstd. In this section
we will restrict our usage of libstd in order to reduce binary size further.
If you want an executable smaller than 20 kilobytes, Rust's string formatting code,
core::fmt must
be removed. panic=immediate-abort only removes some usages of this code. There is a lot of other
code that uses formatting in some cases. That includes Rust's "pre-main" code in libstd.
By using a C entry point (by adding the #![no_main] attribute) , managing stdio manually, and
carefully analyzing which chunks of code you or your dependencies include, you can sometimes
make use of libstd while avoiding bloated core::fmt.
Expect the code to be hacky and unportable, with more unsafe{}s than usual. It feels like
no_std, but with libstd.
Start with an empty executable, ensure
xargo bloat --release --target=... contains no
core::fmt or something about padding. Add (uncomment) a little bit. See that xargo bloat now
reports drastically more. Review source code that you've just added. Probably some external crate or
a new libstd function is used. Recurse into that with your review process
(it requires [replace] Cargo dependencies and maybe digging in libstd), find out why it
weighs more than it should. Choose alternative way or patch dependencies to avoid unnecessary
features. Uncomment a bit more of your code, debug exploded size with xargo bloat and so on.
On macOS, the final stripped binary is reduced to 8KB.
Removing libstd with #![no_std]
[!NOTE]
Example projects are located in the no_std folder.
Up until this point, our application was using the Rust standard library, libstd. libstd
provides many convenient, well tested cross-platform APIs and data types. But if a user wants
to reduce binary size to an equivalent C program size, it is possible to depend only on libc.
It's important to understand that there are many drawbacks to this approach. For one, you'll
likely need to write a lot of unsafe code and lose access to a majority of Rust crates
that depend on libstd. Nevertheless, it is one (albeit extreme) option to reducing binary size.
A striped binary built this way is around 8KB.
#![no_std]
#![no_main]
extern crate libc;
#[no_mangle]
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize {
// Since we are passing a C string the final null character is mandatory.
const HELLO: &'static str = "Hello, world!\n\0";
unsafe {
libc::printf(HELLO.as_ptr() as *const _);
}
0
}
#[panic_handler]
fn my_panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
Compress the binary
[!NOTE]
Up until this point, all size-reducing techniques were Rust-specific. This section describes
a language-agnostic binary packing tool that is an option to reduce binary size further.
UPX is a powerful tool for creating a self-contained, compressed
binary with no addition runtime requirements. It claims to typically reduce binary size by 50-70%,
but the actual result depends on your executable.
$ upx --best --lzma target/release/min-sized-rust
[!WARNING]
There have been times that UPX-packed binaries have flagged heuristic-based antivirus software
because malware often uses UPX.
Tools
cargo-bloat - Find out what takes most of the
space in your executable.
cargo-llvm-lines - Measure the number and size
of instantiations of each generic function, indicating which parts of your code offer the highest
leverage in improving compilation metrics.
cargo-unused-features - Find and prune
enabled but potentially unused feature flags from your project.
momo - proc_macro crate to help keeping the code footprint
of generic methods in check.
Twiggy - A code size profiler for Wasm.
Containers
Sometimes it's advantageous to deploy Rust into containers
(e.g. Docker). There are several great existing resources to help
create minimum sized container images that run Rust binaries.
Official rust:alpine image
mini-docker-rust
muslrust
docker-slim - Minify Docker images
dive - A tool for exploring a container image and
discovering ways to shrink the size of the image.
distroless - 2MB base image to run statically linked Rust program
References
151-byte static Linux binary in Rust - 2015
Why is a Rust executable large? - 2016
Tiny Rocket - 2018
Formatting is Unreasonably Expensive for Embedded Rust - 2019
Tiny Windows executable in Rust - 2019
Making a really tiny WebAssembly graphics demos - 2019
Reducing the size of the Rust GStreamer plugin - 2020
Optimizing Rust Binary Size - 2020
Minimizing Mender-Rust - 2020
Optimize Rust binaries size with cargo and Semver - 2021
Avoiding allocations in Rust to shrink Wasm modules - 2022
A very small Rust binary indeed - 2022
The dark side of inlining and monomorphization - 2023
Making Rust binaries smaller by default - 2024
Tock Binary Size - 2024
Trimming down a rust binary in half - 2024
Reducing WASM binary size: lessons from building a web terminal - 2024
min-sized-rust-windows - Windows-specific tricks to reduce binary size
Shrinking .wasm Code Size
Organizations
wg-binary-size: Working group for improving the size of Rust programs and libraries.
Legacy Techniques
The following techniques are no longer relevant for modern Rust development, but may apply to older
versions of Rust and are maintained for historical purposes.
Remove Jemalloc
[!IMPORTANT]
As of Rust 1.32,
jemalloc is removed by default.
If using Rust 1.32 or newer, no action is needed to reduce binary size regarding this
feature.
Prior to Rust 1.32, to improve performance on some platforms Rust bundled
jemalloc, an allocator that often
outperforms the default system allocator. Bundling jemalloc added around 200KB
to the resulting binary, however.
To remove jemalloc on Rust 1.28 - Rust 1.31, add this code to the top of main.rs:
use std::alloc::System;
#[global_allocator]
static A: System = System;
How does johnthagen/min-sized-rust compare to other Backend projects?
johnthagen/min-sized-rust is tracked by TopGit in the Backend category, with 9.9k GitHub stars and written in Rust. Browse the Backend topic page on TopGit to compare it against similar projects by stars and activity.
Is johnthagen/min-sized-rust open source?
Yes β johnthagen/min-sized-rust ships under the MIT license, which makes its source code freely readable (and, depending on license terms, forkable and reusable). Source: github.com/johnthagen/min-sized-rust.
What else is in the Backend space?
johnthagen/min-sized-rust is tracked by TopGit under the Backend category, alongside 9 GitHub-tagged topics. Trending and Topics pages list peer repositories of comparable stars and language.
What is johnthagen/min-sized-rust?
johnthagen/min-sized-rust (johnthagen/min-sized-rust) is a Rust project on GitHub. From the project's own README: π¦ How to minimize Rust binary size π¦ https://github.com/johnthagen/min-sized-rust
Where do I read more about johnthagen/min-sized-rust?
This TopGit page is a snapshot β the READ ME tab shows the project's own README content (links stripped, images preserved). The GitHub repository at github.com/johnthagen/min-sized-rust is the definitive source.
Why is johnthagen/min-sized-rust categorized under Backend?
TopGit places johnthagen/min-sized-rust in the Backend category based on its GitHub topics and description (tagged: "binary-size", "cargo", "containers"). Categories are assigned from real repository metadata, not editorial guesswork.
Read full README in the tab above.
Curious whether min-sized-rust is right for you?
Let ChatGPT, Claude, or Perplexity look into it β click below and see what AI actually says about min-sized-rust.