How do you choose a base image (alpine vs. slim vs. distroless vs. full)?
Quick Answer
A full base image (e.g., ubuntu, node) includes a complete OS userland with many common tools — largest, but most compatible and easiest to debug. Slim variants strip out documentation, extra utilities, and other non-essential packages, giving a meaningfully smaller image with few compatibility surprises. Alpine uses the musl C library instead of glibc and BusyBox instead of GNU coreutils — often the smallest practical option, but occasionally incompatible with software expecting glibc specifically. Distroless images strip out even the shell and package manager, keeping only the application and its direct runtime dependencies — smallest attack surface, but hardest to debug interactively since there's no shell to exec into.
Detailed Answer
Full images — maximum compatibility, largest size
FROM node:20 # based on a full Debian userland
Includes a complete set of common OS utilities, shells, and libraries. Most software "just works" with no unexpected missing-dependency surprises. Debugging is straightforward too, since every common tool is available — you can just run docker exec -it container bash. The cost is size: often several hundred megabytes before your application's own dependencies are even added.
Slim images — a leaner version of the same distribution
FROM node:20-slim # same Debian base, but with docs, extra utilities, etc. stripped out
Meaningfully smaller than the full variant, while still using the same package ecosystem: glibc and apt. That means very few compatibility surprises, since it's the same distribution family, just trimmed down. A good, low-risk default for most applications wanting a size improvement without changing the C library or package manager.
Alpine images — smallest practical general-purpose option, different internals
FROM node:20-alpine # based on Alpine Linux -- musl libc, BusyBox, apk package manager
Alpine Linux uses musl libc instead of glibc, and BusyBox (a single compact binary providing minimal versions of many standard Unix utilities) instead of the GNU toolchain. This combination makes Alpine-based images dramatically smaller, often 5-10x smaller than the equivalent Debian-based image. The tradeoff: some software behaves subtly differently, or fails to build or run correctly, against musl — particularly anything with native compiled dependencies, or software that assumes glibc-specific behavior. This compatibility risk is real and occasionally time-consuming, so test for it rather than assume it away.
Distroless images — no shell, no package manager, minimal attack surface
FROM gcr.io/distroless/nodejs20-debian12
Google's distroless images strip out everything not strictly needed to run the application — no shell, no package manager, no text editors, not even basic Unix utilities like ls or cat. This gives the smallest possible attack surface: an attacker who compromises the running application has no shell to pivot into and no package manager to install more tools with. But it has a real operational cost. You cannot docker exec -it container sh into a distroless container, since there's no shell binary present. Debugging needs different techniques — attaching ephemeral debug containers alongside it (similar to Kubernetes's kubectl debug), or relying entirely on external logging and observability instead of interactive investigation.
Comparing the tradeoffs
| Full | Slim | Alpine | Distroless | |
|---|---|---|---|---|
| Relative size | Largest | Smaller | Smallest (general-purpose) | Smallest (no shell/tools) |
| C library | glibc | glibc | musl | Varies (often glibc-based) |
| Compatibility risk | Lowest | Low | Moderate (musl-specific issues) | Low (same libc, just missing tools) |
| Interactive debugging | Easiest | Easy | Easy | Not possible (no shell) |
| Attack surface | Largest | Smaller | Smaller | Smallest |
Slim is a safe, low-risk default for most applications. Alpine is worth it once you've actually verified musl compatibility, not just assumed it — a real concern for compiled languages with native extensions. Distroless earns its debugging cost once solid centralized logging already reduces how often you'd need an interactive shell anyway. Full images mostly belong in local development, where the debugging convenience outweighs the size/security cost.