Docker Multi-Stage Builds: Smaller Images, Faster Deploys
A naive Node.js Docker image ships your entire dev environment to production — node_modules, TypeScript compiler, build tools. Multi-stage builds separate build from runtime.
Before: The Naive Dockerfile
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["node", "dist/server.js"]
# Result: ~1.2GB image with all dev dependencies
After: Multi-Stage Build
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # Clean install, faster than npm install
COPY . .
RUN npm run build
# Stage 2: Runtime (only what you need)
FROM node:20-alpine AS runner
WORKDIR /app
# Only copy production dependencies
COPY package*.json ./
RUN npm ci --omit=dev
# Copy compiled output from builder
COPY --from=builder /app/dist ./dist
# Run as non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
# Result: ~180MB image — 85% smaller
Layer Caching Optimization
# Copy package files FIRST — they change rarely
COPY package*.json ./
RUN npm ci # This layer is cached unless package.json changes
# Copy source code LAST — it changes often
COPY . .
RUN npm run build
Wrong order kills cache: every code change triggers npm ci.
.dockerignore
node_modules
.next
dist
.env
.env.local
*.log
.git
Next.js Standalone Mode
// next.config.js
module.exports = { output: 'standalone' };
FROM node:20-alpine AS runner
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
CMD ["node", "server.js"]
# Next.js traces all required files — smallest possible image
Optimized Docker builds, CI/CD pipelines, and deployment configs are included in the Ship Fast Skill Pack as ready-to-use templates.
Top comments (0)