How Tech - Systems Programming

How Tech - Systems Programming

Creating a Minimal Static Binary for Container Environments

Feb 15, 2026
∙ Paid

Last month, our Kubernetes cluster was spending 40 seconds pulling a 200MB container image for a service that’s essentially a 50-line C program. The actual binary? 2.3MB. The Ubuntu base image, glibc, and a dozen unnecessary shared libraries? 197.7MB. We weren’t deploying software—we were shipping an entire operating system to run a glorified HTTP handler.

This is the container bloat problem. Every dynamic binary drags along libc, libpthread, libm, and whatever else the linker found in /lib when you hit build. Then you need a base image with a compatible libc version, a shell for debugging, and suddenly your “minimal” container is 80MB.

Why Dynamic Linking Exists (And Why It’s Wrong for Containers)

Dynamic linking made sense in 1990. You had one copy of libc.so.6 in memory, shared across 50 processes. Saved RAM. Updated the library once, every program got the security fix. Brilliant.

But containers aren’t 1990. Each container gets its own memory namespace. That shared library? Not shared anymore—each pod loads its own copy. You’re not saving memory; you’re wasting network bandwidth and disk space shipping the same glibc across hundreds of nodes.

Worse, dynamic linking breaks at runtime in ways that make you want to throw your laptop out a window. Ever seen GLIBC_2.29 not found in production? That’s your binary looking for symbols that don’t exist because someone deployed your Ubuntu 18.04 build to an Ubuntu 20.04 host. The binary loads, parses the ELF header, the dynamic linker starts up, begins symbol resolution, and then fails spectacularly because libc versions don’t match.

User's avatar

Continue reading this post for free, courtesy of Systems.

Or purchase a paid subscription.
© 2026 Sumedh S · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture