Docker Multi-Stage Builds for Node.js Apps
Reduce your Node.js Docker image size by 80% using multi-stage builds that separate build dependencies from production runtime artifacts.
Tags
Docker Multi-Stage Builds for Node.js Apps
TL;DR
Use a multi-stage Dockerfile to build in a full Node.js image and run in a slim one, cutting your final image size by 80% or more.
The Problem
A typical Node.js Dockerfile installs all dependencies (including devDependencies), copies the full source, and builds in a single image. The result is a 1GB+ image that ships TypeScript, testing tools, build scripts, and source files that your production app never touches. Larger images mean slower deployments, higher storage costs, and a bigger attack surface.
The Solution
Split your Dockerfile into three stages -- dependencies, build, and production:
# Stage 1: Install dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Stage 2: Build the application
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Stage 3: Production image
FROM node:20-alpine AS production
WORKDIR /app
ENV NODE_ENV=production
# Only copy what's needed to run
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./
# Remove devDependencies
RUN npm prune --omit=dev
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]Build and check the size difference:
docker build -t myapp .
docker images myapp # typically 150-200MB vs 1GB+Why This Works
Each FROM statement starts a fresh filesystem layer. The final production stage only contains the alpine base, production node_modules, and compiled output. All build tools (TypeScript, ESLint, testing frameworks) and source files are left behind in the earlier stages. Docker discards those intermediate stages from the final image entirely, so they add zero bytes to what you ship. The npm prune --omit=dev step ensures even transitive devDependencies are stripped out.
Collaboration
Need help with a project?
Let's Build It
I help startups and established companies design, build, and scale world-class digital products. From deep technical architecture to pixel-perfect UI — let's bring your vision to life.
Related Articles
TypeScript Utility Types You Should Know
Five essential built-in generic utility types in TypeScript that will save you hundreds of lines of code.
Generate Dynamic OG Images in Next.js
Generate dynamic Open Graph images in Next.js using the ImageResponse API with custom fonts, gradients, and data-driven content for social sharing.
GitHub Actions Reusable Workflows: Stop Repeating Yourself
Create reusable GitHub Actions workflows with inputs, secrets, and outputs to eliminate YAML duplication across repositories and teams efficiently.