Base images, smallest to largest
| Image | Size | Best for |
|---|---|---|
scratch | 0 bytes | Static binaries (Go, Rust) |
distroless | ~20MB | Hardened production, minimal attack surface |
alpine | ~5-50MB | Go, Rust, simple Node. Tricky for Python (musl). |
*-slim | ~50-150MB | Most apps. The right default. |
full (e.g. python:3.12) | ~500MB-1GB | Dev environments, when you need build tools |
The trade-offs are real
alpine uses musl libc, not glibc. Many Python wheels are glibc-only — pip will fall back to compiling from source on alpine. That can blow your build time up by 10x. For Go and Rust (which produce static binaries), alpine is great. For Python apps, prefer -slim unless you really know what you're getting into.
distroless images have no shell, no package manager. You can't docker exec -it ... bash into them. Great for production hardening. Painful for ad-hoc debugging.
scratch is literally empty. Only static binaries with no dynamic linking work. Maximum minimalism, only for the right tool.