Diet Docker: How to Slash Your Image Size by 90%
I recently audited a client's infrastructure and found a simple Node.js microservice that was 1.2GB in size. Why? Because they blindly copied a FROM node:latest example and included their entire development toolchain in production. We got it down to 80MB.
The "node_modules" Black Hole
The biggest offender is dependencies. You need typescript, webpack, and jest to build your app, but you do NOT need them to run it. If you ship your devDependencies to production, you are shipping dead weight.
The Magic of Multi-Stage Builds
Docker allows you to use multiple FROM statements in a single Dockerfile. The first stage does the heavy lifting (compiling, building). The second stage copies only the artifacts from the first stage.
# Stage 1: Builder FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # Stage 2: Runner FROM node:18-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/package*.json ./ RUN npm install --production CMD ["node", "dist/index.js"]
This pattern discards the entire "Builder" environment. The final image only contains the compiled code and production dependencies.
Alpine Linux
Standard Linux images (like Ubuntu/Debian) include tools you don't need (curl, vim, git). Alpine Linux is a security-oriented, lightweight distro that is only ~5MB. Switching from node:18 to node:18-alpine can save 600MB instantly.
Need to generate a quick, optimized Dockerfile structure? Use our Dockerfile Generator to get the boilerplate syntax right for your stack.
Turn Theory Into Practice
You have read the guide. Now use the professional-grade tools mentioned in this article to optimize your workflow immediately.