I shrunk my Rust binary from 11MB to 4.5MB with bloaty-metafile



This content originally appeared on DEV Community and was authored by 阿豪

TL;DR: Used bloaty-metafile to analyze binary size, disabled default features on key dependencies, reduced size by 59% (11MB → 4.5MB)

The Problem

I’ve been working on easy-install (ei), a CLI tool that automatically downloads and installs binaries from GitHub releases based on your OS and architecture. Think of it like a universal package manager for GitHub releases.

Example: ei ilai-deutel/kibi automatically downloads the right binary for your platform, extracts it to ~/.ei, and adds it to your shell’s PATH.

I wanted to run this on OpenWrt routers, which typically have only ~30MB of available storage. Even with standard release optimizations, the binary was still ~10MB:

[profile.release]
debug = false
lto = true
strip = true
opt-level = 3
codegen-units = 1
panic = "abort"

Not good enough.

The Analysis

I used bloaty-metafile to analyze where the bloat was coming from. Turns out, ~80% of the binary size came from just 20% of dependencies:

  • clap – CLI argument parsing
  • reqwest – HTTP downloads
  • tokio – Async runtime
  • regex – Parsing non-standard release filenames (e.g., biome-linux-x64-muslx86_64-unknown-linux-musl)
  • easy-archive – Archive extraction (tar.gz, zip, etc.)

The Optimization

The key insight: disable default features and only enable what you actually use.

1. clap – Saved 100-200KB

clap = { version = "4", features = ["derive", "std"], default-features = false }

Only enable basic functionality. No color output, no suggestions, no fancy formatting.

2. reqwest – Saved ~4MB (!!)

reqwest = { version = "0.12", features = [
  "json",
  "rustls-tls",  # Instead of native-tls
  "gzip"
], default-features = false }

Switching from native-tls to rustls-tls was the biggest win. Native TLS pulls in system dependencies that bloat the binary significantly.

3. tokio – Saved ~100KB

tokio = { version = "1", features = [
  "macros",
  "rt-multi-thread",
], default-features = false }

Only enable the multi-threaded runtime and macros. No I/O, no time, no sync primitives we don’t use.

4. regex – Saved ~1MB

regex = { version = "1", default-features = false, features = ["std"] }

Since we only use regex occasionally for URL parsing, we can disable Unicode support and other features.

5. easy-archive – Saved ~1MB

Only enable tar.gz decoding, skip encoding and other formats we don’t need.

Results

Before: 11MB
After: 4.5MB

Most crates enable way more than you need. The 80/20 rule applies here – optimizing a few key dependencies can yield massive savings.

Links:


This content originally appeared on DEV Community and was authored by 阿豪