Shrink Docker Images by 90% with Multi-Stage Builds
Multi-stage builds can significantly reduce image size by stripping out everything you don't need at runtime.
What
Multi-stage builds use multiple FROM statements in a single Dockerfile. You compile/build in one stage with all the dev tools, then copy only the final artifact into a minimal runtime image. The build tools, source code, and intermediate files are left behind.
Why It Matters
Smaller images mean faster pulls, faster deployments, reduced storage costs, and a smaller attack surface. If your production image includes compilers, package managers, and source code, you're shipping unnecessary bloat and potential security risks.
Example
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production (only the built output)
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
CMD ["node", "dist/index.js"]Common Mistake
Using a single FROM with a full OS base image like node:20 (approximately 900MB+). Your production image includes npm, the full Node toolchain, and all dev dependencies.
Quick Fix
Add a second FROM stage that copies only what you need. Use Alpine-based images for the runtime stage. Run npm ci --omit=dev in the production stage to skip dev dependencies.
Key Takeaways
- 11 Dockerfile, 2 stages
- 2Stage 1: Build with all dev tools
- 3Stage 2: Copy ONLY the final artifact
- 4COPY --from=builder is the magic
- 5Result: significantly smaller, more secure images
Was this tip helpful?
Help us improve the DevOpsPath daily collection