RepoPilotOpen in app →

johnthagen/min-sized-rust

🦀 How to minimize Rust binary size 📦 https://github.com/johnthagen/min-sized-rust

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 7mo ago
  • 11 active contributors
  • MIT licensed
Show all 7 evidence items →
  • CI configured
  • Slowing — last commit 7mo ago
  • Single-maintainer risk — top contributor 89% of recent commits
  • No test directory detected

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/johnthagen/min-sized-rust)](https://repopilot.app/r/johnthagen/min-sized-rust)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/johnthagen/min-sized-rust on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: johnthagen/min-sized-rust

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/johnthagen/min-sized-rust shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 7mo ago
  • 11 active contributors
  • MIT licensed
  • CI configured
  • ⚠ Slowing — last commit 7mo ago
  • ⚠ Single-maintainer risk — top contributor 89% of recent commits
  • ⚠ No test directory detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live johnthagen/min-sized-rust repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/johnthagen/min-sized-rust.

What it runs against: a local clone of johnthagen/min-sized-rust — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in johnthagen/min-sized-rust | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | Last commit ≤ 246 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>johnthagen/min-sized-rust</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of johnthagen/min-sized-rust. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/johnthagen/min-sized-rust.git
#   cd min-sized-rust
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of johnthagen/min-sized-rust and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "johnthagen/min-sized-rust(\\.git)?\\b" \\
  && ok "origin remote is johnthagen/min-sized-rust" \\
  || miss "origin remote is not johnthagen/min-sized-rust (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 246 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~216d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/johnthagen/min-sized-rust"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

min-sized-rust is a practical guide and runnable example repository demonstrating techniques to minimize compiled Rust binary size. It showcases real-world optimizations including release-mode compilation, symbol stripping, link-time optimization (LTO), no_std and no_main approaches, and custom build profiles—providing concrete before/after examples across Linux, Windows, and Unix platforms. Educational monorepo with four independent example projects: root src/main.rs is the baseline, build_std/ demonstrates custom standard library building, no_std/nix/ and no_std/win/ show no-standard-library approaches by OS, and no_main/nix/ and no_main/win/ demonstrate skipping Rust's entry point—each with separate Cargo.toml and platform-specific configurations.

👥Who it's for

Embedded systems engineers, IoT developers, and anyone shipping Rust binaries where size matters (containerized apps, edge devices, WebAssembly targets). Also useful for Rust learners who want to understand how compilation, linking, and standard library inclusion affect final binary footprint.

🌱Maturity & risk

Actively maintained single-author project (johnthagen) with clear GitHub Actions CI/CD setup (build.yml). The repository is well-documented with a comprehensive README and organized example projects. Not a large community project but represents established, proven patterns—suitable as a reference guide rather than a dependency.

Minimal risk: zero external dependencies (empty [dependencies] section), single-maintainer but low maintenance burden due to narrow scope, no breaking API surface. Main risk is platform-specific advice (Linux strip, Windows-specific no_main paths) may drift with Rust compiler changes—mitigated by CI verification across platforms.

Active areas of work

Repository appears stable; primary activity is maintaining compatibility with Rust compiler releases and CI validation via GitHub Actions. The repo serves as a living guide updated when Rust introduces new size-optimization features (e.g., Rust 1.59's auto-strip support mentioned in README).

🚀Get running

git clone https://github.com/johnthagen/min-sized-rust.git
cd min-sized-rust
cargo build --release
# For size comparison:
ls -lh target/release/min-sized-rust

Daily commands:

# Baseline (debug):
cargo build

# Optimized release:
cargo build --release

# For no_std examples:
cd no_std/nix && cargo build --release
cd ../win && cargo build --release

# For no_main examples:
cd ../../no_main/nix && cargo build --release

🗺️Map of the codebase

  • Cargo.toml: Defines the five core optimizations (opt-level = "z", lto = true, codegen-units = 1, panic = "abort", strip = true) that form the baseline size-optimization strategy.
  • README.md: Complete tutorial-style guide explaining each optimization technique with minimum Rust version requirements, use cases, and trade-offs (speed vs. size).
  • .github/workflows/build.yml: CI pipeline that verifies all example projects compile correctly on Linux, macOS, and Windows—ensures optimizations remain valid across platform updates.
  • build_std/Cargo.toml: Demonstrates -Z build-std nightly feature to rebuild the standard library itself with optimizations, enabling deeper size reductions than default std.
  • no_std/nix/src/main.rs: Minimal example using #![no_std] and #![no_main] on Linux, showing how to eliminate Rust's standard library entirely for embedded or ultra-minimal binaries.
  • no_main/win/src/main.rs: Windows-specific example of no_main that provides a custom entry point, demonstrating platform-specific bootstrap code needed to avoid Rust's default runtime.

🛠️How to make changes

Adding a new optimization example: Create a new directory (e.g., wasm_opt/) with its own Cargo.toml and src/main.rs, following the pattern in build_std/ or no_std/. Updating documentation: Edit README.md with concrete binary size measurements (use ls -lh or stat on compiled binaries). Testing platform support: Modify .github/workflows/build.yml to test your changes on Linux, macOS, and Windows. Tweaking global settings: All size optimizations are in the root Cargo.toml [profile.release] section.

🪤Traps & gotchas

Nightly features: build_std/ requires +nightly toolchain (not stable Rust). Platform specificity: no_std examples require matching OS (nix/ for Linux/macOS, win/ for Windows); cross-compilation has additional complexity. Linking assumptions: Symbol stripping (strip = true) requires Rust 1.59+; pre-1.59 needs manual strip command post-build. Panic behavior change: panic = "abort" disables unwinding (changes error handling semantics)—incompatible with code expecting exception-like behavior. LTO trade-off: lto = true significantly increases compile time; full LTO (lto = "fat") can take 2-10x longer to build.

💡Concepts to learn

  • Link Time Optimization (LTO) — LTO enables cross-module inlining and dead-code elimination at link time (not compile time), often reducing binary size 5-20% but with substantial compile-time cost—a key trade-off decision shown in this repo.
  • Codegen Units — Controlling codegen-units (set to 1 in this repo) trades parallel compilation speed for optimization quality—fewer units allow the compiler to inline and eliminate more code across unit boundaries.
  • Symbol Stripping — Debug symbols (.symtab, .debug sections in ELF binaries) are not needed for execution; stripping removes 30-50% of binary size on Linux/macOS without affecting runtime behavior.
  • no_std and no_main — Opting out of Rust's standard library (no_std) and runtime initialization (no_main) eliminates 500KB+ of code for embedded/minimal environments—the most aggressive size reduction technique shown.
  • Optimization Levels (opt-level z vs s) — Rust provides opt-level = "z" (aggressive size) and "s" (moderate size); choosing between them requires empirical testing since different codebases optimize differently—this repo demonstrates the variability.
  • Panic Strategy (abort vs unwind) — Setting panic = "abort" removes unwinding machinery and exception tables, reducing binary size by 1-2%; trade-off is loss of drop semantics and panic handling—appropriate only for specific use cases.
  • Custom Build Profiles — Cargo profiles ([profile.release] section) allow fine-grained control over optimization flags per compilation mode—the foundation of all techniques demonstrated in this repo.
  • rust-lang/rust — Official Rust compiler repository; upstream source for all optimization flags and binary size behavior discussed in this guide.
  • fermyon/spin — Production WebAssembly runtime that heavily relies on minimal binary size for edge deployment—applies many techniques from min-sized-rust in practice.
  • japaric/cortex-m — Embedded ARM Cortex-M crate for microcontrollers; demonstrates no_std patterns and size constraints that min-sized-rust examples prepare developers for.
  • rust-osdev/bootloader — OS bootloader written in minimal Rust showing extreme size optimization for code that must fit in limited boot sectors—extends no_std/no_main concepts.
  • bytecodealliance/wasmtime — WebAssembly runtime where binary size is critical for embedding; real-world consumer of the optimization techniques documented in min-sized-rust.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add binary size regression tests in CI with size benchmarks

The repo demonstrates binary size minimization techniques but lacks automated size verification in .github/workflows/build.yml. Currently, there's no mechanism to prevent regressions or track how each technique (build_std, no_main, no_std) affects final binary size. Adding a workflow step that builds each example and asserts maximum size thresholds would catch accidental size increases and provide quantitative evidence of each approach's effectiveness.

  • [ ] Update .github/workflows/build.yml to build all examples (src/main.rs, build_std/, no_main/nix/, no_main/win/, no_std/nix/, no_std/win/)
  • [ ] Add a script that extracts binary sizes and compares against baseline thresholds
  • [ ] Add assertion steps in CI that fail if any binary exceeds expected size limits
  • [ ] Document size expectations in README.md for each approach

Create a comprehensive comparison table in README with measured binary sizes

The README.md is incomplete (cuts off mid-sentence at 'bu') and lacks a comparative analysis table showing actual binary sizes for each technique folder (build_std, no_main/nix, no_main/win, no_std/nix, no_std/win) versus the baseline src/main.rs. Contributors and users need to understand which approach yields the smallest binaries and the tradeoffs involved. A concrete data table would make the repo immediately useful.

  • [ ] Complete the cut-off sentence in README.md
  • [ ] Build all example projects and measure final binary sizes
  • [ ] Create a results table showing: Project | Platform | Binary Size | Techniques Used | Minimum Rust Version
  • [ ] Add explanation of why each approach is smaller and any caveats (e.g., platform-specific limitations in no_main/win vs no_main/nix)

Add platform-agnostic no_std example alongside platform-specific variants

The current structure has no_std/nix/ and no_std/win/ as separate implementations, but there's no unified example showing a truly portable no_std approach (e.g., using conditional compilation). New contributors may be confused about whether no_std requires platform-specific code. Adding a single no_std example with #[cfg(target_os = ...)] blocks would demonstrate how to write portable minimal binaries and reduce duplication across no_std/nix/ and no_std/win/ source files.

  • [ ] Create no_std/src/main.rs with platform-specific code blocks using target_os conditionals
  • [ ] Create a parent-level no_std/Cargo.toml that points to src/main.rs
  • [ ] Verify the unified example produces equivalent binary sizes to current platform-specific variants
  • [ ] Update README.md to explain when to use unified vs platform-specific approaches

🌿Good first issues

  • Add a wasm/ example demonstrating size optimizations for WebAssembly targets—profile and document binary sizes using wasm-opt and compare against baseline techniques.
  • Expand the no_std examples with a concrete use case (e.g., a tiny HTTP server or GPIO toggle for microcontroller) and benchmark binary sizes against equivalent C implementations.
  • Create a Makefile or shell script at the root that builds all example projects in sequence and generates a comparison table of binary sizes with each optimization technique disabled/enabled.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 24233f9 — Fix regression of switching to new panic=immediate-abort flag (#87) (FooIbar)
  • eae3cd5 — Switch to new panic=immediate-abort flag (#86) (samestep)
  • b0bc6dc — Add distroless to README.md (#84) (Xerxes-2)
  • de33863 — Add tool reference to cargo-llvm-lines (johnthagen)
  • d2a8cd9 — Dynamic linking (#80) (Kivooeo)
  • f1db29c — Add reference to "Reducing WASM binary size: lessons from building a web terminal" (johnthagen)
  • f71ee5d — Add reference to Trimming down a rust binary in half (johnthagen)
  • 8dcb3a0 — Add -Zfmt-debug=none to nightly CI (#73) (johnthagen)
  • cbab928 — Move jemalloc to a legacy techniques section (#72) (johnthagen)
  • dc323be — Wrap README at 100 lines (johnthagen)

🔒Security observations

This repository is a demonstration project for Rust binary size optimization with minimal dependencies, presenting a low attack surface. No critical or high-severity vulnerabilities were identified. The codebase has no external dependencies, which eliminates supply chain risks. Minor concerns relate to production debugging capabilities and resource cleanup due to aggressive optimization settings. The project is suitable for educational purposes but production deployments should reconsider the panic='abort' and strip=true settings to maintain debuggability and proper error handling. Overall security posture is strong for a minimalist project.

  • Low · Debug Symbols Stripped in Release Build — Cargo.toml [profile.release]. The Cargo.toml configuration includes 'strip = true' which removes all debug symbols from the binary. While this reduces binary size, it makes debugging and crash analysis significantly more difficult in production environments, potentially impacting incident response capabilities. Fix: Consider maintaining debug symbols in a separate debug info file or providing symbol servers for production binaries. Only strip symbols if the application is not mission-critical or if alternative debugging mechanisms are in place.
  • Low · Panic Behavior Set to Abort — Cargo.toml [profile.release]. The configuration 'panic = "abort"' causes the application to immediately abort on panic without unwinding the stack. This prevents proper cleanup of resources, graceful shutdown, or error recovery mechanisms that might be in place. Fix: Use 'panic = "unwind"' (default) to allow proper resource cleanup and graceful error handling. Only use 'abort' if the application cannot tolerate any unwinding overhead and proper cleanup is not required.
  • Low · Aggressive LTO Configuration — Cargo.toml [profile.release]. The configuration uses 'codegen-units = 1' which disables parallel codegen and increases compilation time significantly. Combined with 'lto = true', this creates a lengthy build process that may impact CI/CD pipeline efficiency and developer productivity. Fix: Consider using 'codegen-units = 16' (default) for development builds and only apply 'codegen-units = 1' in CI/CD pipelines for final releases. This balances binary size optimization with build performance.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · johnthagen/min-sized-rust — RepoPilot