Building a single static binary that runs on every platform you actually use
“Single static binary” is one of those phrases that sounds simpler than it is. The marketing version is “one file, drop it on the box, run it.” The engineering version is “one file, drop it on the box, hope you guessed the libc right, hope the kernel surface is close enough, hope the auxiliary services it expects are wired up, hope the user does not need a feature gated behind a system library you decided not to link.”
We shipped rewget as something close to a single static binary. The CLI is a binary; the daemon is a second binary that the CLI can start on demand; both fall out of the same Cargo workspace and the same release pipeline. This post is about what that actually means in 2026, where the line between “works” and “claims to work” sits, and what we decided to draw it at.
What “static” buys you
A static binary buys you three things, and the order they show up in matters.
First, simpler distribution. You publish one file per (OS, architecture) pair. The installer reads “download, untar, move to /usr/local/bin, mark executable, done.” No package manager metadata, no install scripts, no postinst hooks. The thing the user runs is the same thing your CI produced.
Second, fewer runtime surprises. Dynamic linking gives you a graph of “what if” — what if the system glibc is a different version, what if libssl is missing, what if the user has set LD_LIBRARY_PATH for some reason that made sense to them. Static linking flattens that graph. The binary either runs or it does not, and if it does not, the answer is almost always “wrong architecture” or “kernel too old,” both of which are cheap to diagnose.
Third, a marketing line that resonates with the audience. “Single binary” is a reliable signal that the author thought about distribution. People who have lived through node_modules or python dependency hell read it as “this is going to respect my time.” That is not nothing.
What it does not buy you is true platform-independence. Linux binaries do not run on macOS. macOS binaries do not run on Linux. Windows is a different lineage entirely. Static linking is about cleanliness within a target, not about portability across targets.
What we actually ship
The rewget release artifacts are platform-pinned. The README lists Linux and macOS as the supported platforms. Inside that:
- The CLI binary,
rewget, is built withcargo build --releaseusing a profile that pins LTO, strips symbols, and sets codegen-units to 1. The release page hosts one binary per (OS, arch) pair we cut for. - The daemon binary,
rewgetd, ships alongside it from the same workspace. The CLI starts it on demand; the daemon owns the browser pool and the TLS session cache. - Browser fingerprint profiles are Ed25519-signed and distributed as a separate update channel via
--rewget-update-profiles. We treat them as data, not as code, because Chrome and Firefox cut releases on their own schedule.
The headless Chromium dependency for stage 3 is not bundled. We decided not to ship a 200-megabyte browser inside the release tarball; instead, stage 3 expects Chromium to be discoverable on PATH or installed by the user. If you never hit stage 3, you never need it. If you do, the install docs cover it.
What we did not do, and why
We did not ship a Windows-native binary. The README and our marketing pages reflect that. The reason is not philosophical: it is that “supports Windows” is a real commitment to a CI matrix, a code-signing flow, and an issue-triage surface that we are not ready to make until the Linux and macOS paths are well-worn. We would rather under-claim and over-deliver than the reverse.
We did not ship a single statically linked monolith with the daemon embedded. Daemonization on Linux is well-understood; on macOS the launchd path is well-understood; embedding it inside the CLI binary would have collapsed both into a worse version of either. The two-binary design lets the daemon survive across CLI invocations, which is what makes the per-domain cache useful.
We did not ship ARM Linux as a tier-1 target in the first release. People sometimes assume “single binary” implies “every CPU.” It does not. Aarch64 Linux is a target we plan to cut, but only after we have the macOS arm64 and x86_64 Linux paths through enough mileage that we trust the release pipeline.
We did not statically link against musl by default. We considered it; musl-linked binaries are the cleanest distribution story on Linux. We ended up shipping glibc-linked binaries for the default Linux artifact because they cover the boxes our users actually run, and a musl variant for the people who want it. The cost was one extra CI job.
The platforms you actually use
The framing in the post title is intentional. “Every platform you actually use” is a much smaller set than “every platform that exists.” For most teams running rewget, the box is one of: an x86_64 Linux server in a cloud region, a macOS laptop on the engineer’s desk, a macOS arm64 laptop on the engineer’s desk, or a CI runner that is one of those. Covering those four platforms covers ninety-something percent of the world we are trying to be useful in.
The other ten percent is real — embedded Linux on aarch64, BSD variants, Windows for legacy CI, and so on — but it is also expensive. The right policy, in our experience, is to be honest about which platforms have been tested and released versus which platforms are reasonable bets that we have not done the work on yet. The README lists the tested set. Everything outside that set is “you might be able to compile it from source.”
The Rust-specific easy mode
A side note for anyone reading this and thinking about doing the same thing: writing rewget in Rust made the static-binary story dramatically easier than it would have been in Go, Python, or Node. Cargo’s release profile is one config block. Cross-compilation between x86_64 Linux and macOS targets is mostly cargo zigbuild or cross with a matching toolchain. Stripping, LTO, mimalloc as the allocator — all of these are flags you set once.
The flip side: the dependency graph for stage 2 (rquest) and stage 3 (chromiumoxide) is large. We took the LTO cost in exchange for the smaller binary. If we needed faster builds for CI we would change the profile, not the dependencies.
What this means for you
If you are choosing between “install this from a package manager” and “drop this binary,” our experience says drop the binary. Package managers add a layer of indirection that, for tools like this, mostly hurts: you get a stale version, or the dependencies the maintainer chose differ from the ones the author tested, or the upgrade path is gated behind a distro release cycle that has nothing to do with your release cycle.
If you are building a CLI tool yourself: static binaries are the right default in 2026. They are not portable across platforms by themselves — you still cut one per target — but they are portable across boxes within a platform, which is what actually matters for distribution.
If you maintain the boxes rewget runs on: pin the version. Read the release notes. Update on your schedule. The two-binary CLI-plus-daemon design means the daemon can pick up its own update without the CLI binary moving, which is sometimes useful in environments where the path to /usr/local/bin is more controlled than ~/.local/share/rewget.
The single static binary is not a magic trick. It is a discipline. Pick your platforms, cover them well, and tell the truth on the marketing page about what is and is not in the release. The audience for this kind of tool is reading the README, and the README is what we got right.