DeployU
Interviews / Cloud & DevOps / This Docker image is 2GB. Optimize it to under 100MB using multi-stage builds.

This Docker image is 2GB. Optimize it to under 100MB using multi-stage builds.

practical Dockerfile Optimization Interactive Quiz Code Examples

The Scenario

Your team inherited a Node.js microservice with this Dockerfile:

FROM node:18
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/server.js"]

The resulting image is 2.1GB. Your DevOps lead wants it under 100MB for faster deployments and reduced storage costs. The image takes 3 minutes to pull on each deployment, causing slow rollouts.

The Challenge

Optimize this Dockerfile to reduce the image size by 95%+ while maintaining functionality. Explain each optimization technique and why it works.

Wrong Approach

A junior engineer might just switch to node:18-slim saving some space, add a .dockerignore file, or try to delete node_modules after build. These approaches fail because node:18-slim is still 200MB+, .dockerignore helps but doesn't address the fundamental issue, and you can't delete files from previous layers - they're still in the image history.

Right Approach

A senior engineer uses multi-stage builds to separate the build environment from the runtime environment. The build stage has all dev dependencies and build tools, while the final stage only contains the compiled application and production dependencies. Combined with a minimal base image like node:18-alpine or distroless, this reduces the image from 2GB to under 100MB.

Step 1: Understand Why the Image is Large

# Analyze image layers
docker history your-app:latest

# Check what's taking space
docker run --rm -it your-app:latest du -sh /*

Common culprits:

  • Full Node.js image with npm, yarn, build tools (~900MB)
  • Development dependencies in node_modules (~500MB+)
  • Source files, tests, documentation (~100MB+)
  • Build artifacts and cache files

Step 2: Implement Multi-Stage Build

# ============================================
# Stage 1: Build
# ============================================
FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files first (better layer caching)
COPY package*.json ./

# Install ALL dependencies (including devDependencies)
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build

# ============================================
# Stage 2: Production
# ============================================
FROM node:18-alpine AS production

# Add non-root user for security
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

# Copy only production dependencies
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# Copy built application from builder stage
COPY --from=builder /app/dist ./dist

# Use non-root user
USER nodejs

EXPOSE 3000

CMD ["node", "dist/server.js"]

Step 3: Add .dockerignore

# .dockerignore
node_modules
npm-debug.log
Dockerfile*
.dockerignore
.git
.gitignore
README.md
.env*
coverage
.nyc_output
*.test.js
*.spec.js
__tests__
docs

Step 4: Verify the Optimization

# Build and check size
docker build -t your-app:optimized .
docker images | grep your-app

# Before: 2.1GB
# After:  ~80MB (96% reduction!)

Size Comparison

ApproachImage SizeReduction
Original (node:18)2.1 GB-
node:18-slim450 MB78%
node:18-alpine180 MB91%
Multi-stage + alpine80 MB96%
Distroless50 MB98%

Advanced: Using Distroless Images

For maximum security and minimum size:

# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage with distroless
FROM gcr.io/distroless/nodejs18-debian11

WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

EXPOSE 3000
CMD ["dist/server.js"]

Distroless benefits:

  • No shell, package manager, or unnecessary binaries
  • Smaller attack surface
  • ~50MB final image size

Practice Question

What is the primary benefit of using multi-stage Docker builds?