Docker Multi-Stage Builds: Smaller Images, Faster Deploys
A naive Node.js Docker image includes dev dependencies, build tools, and TypeScript source — 1GB+. Multi-stage builds compile in one stage and copy only the artifacts to the final image — 150MB.
Single Stage (What Not to Do)
FROM node:20
WORKDIR /app
COPY . .
RUN npm install # Dev deps included
RUN npm run build # Build tools included
CMD ["node", "dist/index.js"]
# Image size: ~1.2GB
Multi-Stage Build
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # Install ALL deps (including dev)
COPY . .
RUN npm run build # Compile TypeScript
# Stage 2: Production
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev # Production deps only
COPY --from=builder /app/dist ./dist # Only compiled output
USER node # Don't run as root
CMD ["node", "dist/index.js"]
# Image size: ~180MB
Next.js Dockerfile
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# Next.js standalone output
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
USER node
EXPOSE 3000
CMD ["node", "server.js"]
# Image size: ~120MB
Enable Standalone Output (Next.js)
// next.config.ts
export default {
output: 'standalone',
};
.dockerignore
node_modules
.next
.git
*.md
.env*
coverage
Layer Caching Optimization
# Copy package files FIRST — changes rarely
COPY package*.json ./
RUN npm ci
# Copy source AFTER — changes often, invalidates cache from here
COPY . .
RUN npm run build
# Result: npm install only reruns when package.json changes
Docker Compose for Local Dev
# docker-compose.yml
services:
app:
build: .
ports: ['3000:3000']
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/mydb
depends_on: [db]
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Docker deployment is pre-configured in the Ship Fast Skill Pack — /deploy skill generates optimized Dockerfiles for your stack. $49 at whoffagents.com.
Top comments (0)