SyntaxStudy
Sign Up
Docker Multi-Stage Builds Explained
Docker Beginner 1 min read

Multi-Stage Builds Explained

Multi-stage builds use multiple FROM instructions in a single Dockerfile, where each FROM begins a new build stage with its own filesystem. The key feature is COPY --from=stage, which copies files from an earlier stage's filesystem into the current stage. This means the final image contains only what you explicitly copy — all build tools, compiler toolchains, test dependencies, and intermediate files are left behind in earlier stages. Before multi-stage builds, teams used two separate Dockerfiles (one for building, one for production) or complicated shell scripts to extract build artifacts. Multi-stage builds solve this cleanly in a single Dockerfile that is version-controlled with the application. The build is reproducible and self-contained. A typical Node.js multi-stage build has three stages: deps (install production dependencies), build (install dev dependencies and run the build step), and production (fresh base image with only the compiled output and production node_modules). The production image has no TypeScript compiler, no test framework, and no source maps — just the built application.
Example
# Three-stage Node.js / TypeScript Dockerfile

# Stage 1: install production deps
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

# Stage 2: compile TypeScript
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm run build

# Stage 3: production image
FROM node:20-alpine AS production
RUN apk add --no-cache dumb-init
WORKDIR /app

COPY --from=deps  /app/node_modules ./node_modules
COPY --from=build /app/dist         ./dist
COPY package.json ./

RUN adduser -S appuser && chown -R appuser /app
USER appuser

ENV NODE_ENV=production PORT=3000
EXPOSE 3000

HEALTHCHECK --interval=30s CMD wget -qO- http://localhost:3000/health||exit 1
ENTRYPOINT ["dumb-init","--"]
CMD ["node","dist/server.js"]