Operations 20 min read

How to Shrink Docker Images by Up to 99% Across Go, Java, Rust, and More

This article explains practical techniques for drastically reducing Docker image sizes—including Go binary static linking, Alpine vs. slim base images, multi‑stage builds for Java, Rust, and interpreted languages, and detailed size comparisons—so developers can choose the optimal base image and build strategy for any language.

Programmer DD
Programmer DD
Programmer DD
How to Shrink Docker Images by Up to 99% Across Go, Java, Rust, and More

1. Go language image slimming

The Go compiler bundles all required dependencies into the binary, but some packages (e.g., DNS) rely on the system's standard library via cgo. Importing net creates a dynamic binary that needs system libraries; you can either copy those libraries into the image or use a busybox:glibc base image.

Disabling cgo (by setting CGO_ENABLED=0) forces Go to use its own implementations, producing a fully static binary that can run in a scratch image:

FROM golang
COPY whatsmyip.go .
ENV CGO_ENABLED=0
RUN go build whatsmyip.go

FROM scratch
COPY --from=0 /go/whatsmyip .
CMD ["./whatsmyip"]

You can also keep cgo but specify built‑in libraries with the -tags netgo flag:

$ go build -tags netgo whatsmyip.go

2. Alpine image deep dive

Alpine is a tiny Linux distribution using the apk package manager. Compared with Ubuntu, Debian, or Fedora, Alpine provides only about 10 000 packages versus >50 000, resulting in a base image size of ~5 MB.

Installation speed is also fast; a simple benchmark shows installing tcpdump takes 1–2 seconds on Alpine, while the same operation takes 7–60 seconds on larger distributions.

Base image           Size      Time to install tcpdump
---------------------------------------------------------
alpine:3.11          5.6 MB    1-2s
archlinux:20200106 409 MB    7-9s
centos:8            237 MB    5-6s
debian:10           114 MB    5-7s
fedora:31           194 MB    35-60s
ubuntu:18.04        64 MB     6-8s

When using Alpine as the second stage ( run) or for all stages, you must consider its use of musl libc instead of glibc. Binaries compiled against musl run fine, but those linked to glibc will fail unless you install the appropriate compatibility libraries.

Run stage using Alpine

FROM gcc AS mybuildstage
COPY hello.c .
RUN gcc -o hello hello.c

FROM alpine
COPY --from=mybuildstage hello .
CMD ["./hello"]

The container may fail with

standard_init_linux.go:211: exec user process caused "no such file or directory"

because Alpine uses musl libc, which is not binary‑compatible with glibc.

All stages using Alpine

For Go you can use golang:alpine as the build stage and copy the static binary into a final alpine image (≈7.5 MB). For C you need to install build-base in the first stage:

FROM alpine
RUN apk add build-base
COPY hello.c .
RUN gcc -o hello hello.c

FROM alpine
COPY --from=0 hello .
CMD ["./hello"]
Install build-base (equivalent to Ubuntu's build-essential ) to get the compiler, standard library, and make.

3. Java image slimming

Java programs run on a JVM, which dynamically links to the standard library (JAR/WAR files). To produce a smaller image you typically use a multi‑stage build: a JDK in the build stage and a JRE in the run stage.

Popular base images include:

openjdk:8-jre-alpine (85 MB)

openjdk:11-jre (267 MB) or openjdk:11-jre-slim (204 MB)

openjdk:14-alpine (338 MB)

Example Java "Hello, world" Dockerfile:

FROM openjdk:11-jdk-alpine
COPY hello.java .
RUN javac hello.java

FROM openjdk:11-jre-alpine
COPY --from=0 hello.class .
CMD ["java", "hello"]

4. Interpreted language image slimming

For languages like Node, Python, or Rust, Alpine works well only when the program uses the standard library and does not require external C dependencies. When such dependencies exist, you either need Alpine‑specific packages (rare) or you must compile them yourself, which defeats the size‑saving purpose.

Python data‑science stacks (numpy, pandas, matplotlib) are compiled against glibc, so they fail on Alpine unless you install many build tools, dramatically slowing the build.

When Alpine is unsuitable, a xxx:slim image (based on Debian/glibc) is a good compromise, offering a smaller footprint while still providing the necessary libraries.

Image            Size
---------------------------
node             939 MB
node:alpine      113 MB
node:slim        163 MB
python           932 MB
python:alpine    110 MB
python:slim      193 MB
ruby             842 MB
ruby:alpine       54 MB
ruby:slim        149 MB

5. Rust image slimming

Rust binaries dynamically link to libdl and need a glibc‑based base image (e.g., Ubuntu, Debian). They cannot run in busybox:glibc because that image lacks libdl. Using rust:alpine works if you compile with musl libc for a fully static binary, which can then be placed in a scratch image.

6. Summary

The optimal base image depends on the language and its dependencies. Alpine yields the smallest images for many compiled languages when static linking is possible, but for interpreted languages with heavy C‑based dependencies, slim or full‑size images may be more practical. Experimentation and experience will guide the right choice.

Footnotes

Two clever tricks to reduce Docker image size by 99%: https://fuckcloudnative.io/posts/docker-images-part1-reducing-image-size/

Natanel Copa's talk: https://dockercon.docker.com/watch/6nK1TVGjuTpFfnZNKEjCEr

musl documentation: https://wiki.musl-libc.org/functional-differences-from-glibc.html

Amazon Corretto: https://hub.docker.com/_/amazoncorretto

Repository with examples: https://github.com/jpetazzo/minimage

Python wheel format: https://pythonwheels.com/

Alpine Python build slowdown article: https://pythonspeed.com/articles/alpine-docker-python/

Rust static linking guide: https://doc.rust-lang.org/1.9.0/book/advanced-linking.html#static-linking

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaDockerimage-optimizationGoAlpinemulti-stage-build
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.