Three Simple Tricks for Smaller Docker Images: Multi‑Stage Builds, Distroless, and Alpine
This article explains how to reduce Docker image size by consolidating RUN commands, using multi‑stage builds, and switching to minimal base images such as distroless or Alpine, providing concrete Node.js examples, size comparisons, and guidance on choosing the right base image for production.
When building Docker containers, a smaller image speeds up transfer and deployment, but each RUN statement creates a new layer and intermediate files, increasing size.
Combining commands with && reduces layers, yet the most effective method is using multi‑stage builds, which allow you to copy only the final artifacts into a clean image.
Example: a simple Node.js app (index.js and package.json) is first built in a stage that installs dependencies, then copied into a second stage. The resulting image has far fewer layers and is considerably smaller.
FROM node:8 as build
WORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM node:8
COPY --from=build /app /
EXPOSE 3000
CMD ["npm", "start"]Running docker build -t node-multi-stage . and inspecting the history shows only three layers in the final image, compared to five layers in a single‑stage build.
Further size reduction can be achieved with distroless images, which contain only the application and its runtime dependencies, omitting package managers, shells, and other binaries. Switching the final stage to gcr.io/distroless/nodejs reduces the image to about 76 MB.
FROM node:8 as build
WORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM gcr.io/distroless/nodejs
COPY --from=build /app /
EXPOSE 3000
CMD ["index.js"]Alternatively, using an Alpine base image ( node:8-alpine ) yields an even smaller image (~70 MB) while still providing a shell for debugging. However, Alpine uses musl libc instead of glibc , which can cause compatibility issues with pre‑compiled binaries.
FROM node:8 as build
WORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM node:8-alpine
COPY --from=build /app /
EXPOSE 3000
CMD ["npm", "start"]Choosing the right base image depends on priorities: distroless offers the smallest footprint and better security (no shell), Alpine offers a tiny size with a shell but may have compatibility challenges, and the original Ubuntu‑based image is larger but provides a full development environment.
Size comparison:
node:8 – 681 MB
node:8 with multi‑stage – 678 MB
distroless nodejs – 76.7 MB
node:8‑alpine – 69.7 MB
In production, prioritize security with distroless; for debugging or when size is the only concern, Alpine may be preferable; the full Ubuntu image remains useful for testing and development.
Qunar Tech Salon
Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.