Why size matters
Smaller images are faster to build, faster to download, and faster to start. They also have a smaller "attack surface" - fewer things installed means fewer things that can have security problems. A few simple habits make a big difference.
Pick a smaller base image
The base image you choose in FROM sets your starting size. Many official images
offer smaller variants:
-
slimvariants strip out extras. For example, Debian offersdebian:bookworm-sliminstead of the fulldebian:bookworm. -
alpinevariants are built on Alpine Linux and are tiny (for examplephp:8.4-fpm-alpineornode:22-alpine).
For example, switching a Node image:
# larger
FROM node:22
# much smaller
FROM node:22-alpine
Alpine images are small but use a slightly different system underneath, so occasionally a package needs extra tweaking. When that's not a problem, they're a great default.
Combine RUN steps and clean up
Remember that each instruction is a layer. Every RUN that installs something adds to
the image, and leftover package caches stay in that layer forever. Combine related
commands and clean up in the same RUN:
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
The rm -rf /var/lib/apt/lists/* deletes the package index cache in the same layer,
so it never bloats the image. If you cleaned up in a separate RUN, the files
would still exist in the earlier layer.
Use a .dockerignore
We covered this earlier, but it's worth
repeating as a best practice: a good .dockerignore keeps big folders like node_modules
and vendor out of your build
context, so they aren't copied into the image by accident.
These three habits - a slim base, combined-and-cleaned RUN steps, and a
.dockerignore - already shrink most images a lot. The next lesson introduces a more
powerful technique: multi-stage builds.
Measure before you optimize
Before chasing a smaller image, look at what you actually have: docker images shows
each image's size, and docker history <image> breaks it down layer by layer so you can
see which instruction added the most weight. Nine times out of ten the bloat is one
obvious thing - a full base image, a copied node_modules, or a leftover package cache -
and fixing that one layer beats a dozen micro-optimizations. Optimize what the numbers
point at, not what you guess.
FAQ
Why are my Docker images so large?
Usually a heavy base image, copied dependency folders (node_modules, vendor), or
package caches left inside a layer. Run docker history <image> to see which layer is
biggest, then target that.
Does alpine always make images smaller?
It usually does, because Alpine Linux is tiny. The trade-off is that it uses a different system library, so occasionally a package needs extra setup. When that's not an issue, it's an excellent default.
How do I check the size of a Docker image?
Run docker images for the total size of each image, and docker history <image> to
see how much each layer contributes.