mediumDevOps EngineerTechnology
How do Docker layers work, and how would you optimize a Dockerfile for a Node.js application?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a DevOps-focused company:
"Our Docker images are 1.2GB and take 8 minutes to build. Builds are slow because every code change reinstalls all npm packages. How do we optimize this?"
Suggested Solution
Docker Layers
Each RUN, COPY, ADD instruction creates a layer. Docker caches layers — if nothing changed, it reuses the cache.
# ❌ BAD: Code changes invalidate npm install cache
FROM node:20
WORKDIR /app
COPY . . # Everything copied → layer changes on any file change
RUN npm install # Re-runs on every code change!
CMD ["node", "server.js"]
# Image size: 1.2GB, Build time: 8min
# ✅ GOOD: Cache npm install separately
FROM node:20-alpine # Alpine = smaller base (180MB vs 1.1GB)
WORKDIR /app
# Copy dependency files FIRST (changes rarely)
COPY package.json package-lock.json ./
RUN npm ci --only=production # Cached unless package.json changes
# Copy source code LAST (changes frequently)
COPY . .
CMD ["node", "server.js"]
# Image size: 250MB, Build time: 30s (with cache)
Multi-stage Build (production)
# 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 (smaller, no dev dependencies)
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]
# Final image: ~180MB (no build tools, no dev deps)
Optimization Summary
| Technique | Impact |
|---|---|
| Alpine base image | -70% size |
| Order COPY for cache hits | -90% build time |
| Multi-stage build | Removes build tools |
.dockerignore | Excludes node_modules, .git, docs |
npm ci instead of npm install | Faster, deterministic |
node_modules
.git
*.md
.env
.next