โRefactor my single-stage Node.js Dockerfile into a multi-stage build: a build stage that installs dependencies and a slim runtime stage that copies only what's needed.โ
03 โ Refactor to multi-stage Dockerfile
Category: Dockerfile ยท Priority: High ยท Step: 4
Build stage installs deps; runtime stage copies only what's needed. This reduces final image size significantly.
๐ฏ What is this task?
Your first Dockerfile works, but it creates a large image because it keeps everything including dev tools inside. A multi-stage build splits the process into two phases: a "build" stage (installs deps, compiles if needed) and a "runtime" stage (only copies the final files). The result is a much smaller, cleaner image.
Think of it like packing for a trip: you lay out everything first (build stage), then only put what you actually need in the suitcase (runtime stage).
๐ค AI Prompt
"Rewrite my Node.js Dockerfile using a multi-stage build. Stage 1 should be called 'builder' and install all dependencies. Stage 2 should be the final runtime image using
node:20-alpine(smaller base), copying onlynode_modulesandindex.jsfrom the builder stage. Add comments explaining why each stage exists."
๐ Step-by-step
Step 1 โ Understand what changes
The old Dockerfile had one FROM. The new one has two. Docker only keeps the last stage in the final image โ the builder stage is thrown away after it's used.
Step 2 โ Replace your Dockerfile content
# ---- Stage 1: Builder ----
# This stage installs all dependencies
FROM node:20 AS builder
WORKDIR /app
# Copy package files and install deps
COPY package*.json ./
RUN npm install
# Copy all source code
COPY . .
# ---- Stage 2: Runtime ----
# This is the final image โ smaller and cleaner
FROM node:20-alpine
WORKDIR /app
# Copy only what we need from the builder stage
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/index.js ./index.js
COPY --from=builder /app/package.json ./package.json
EXPOSE 3000
CMD ["node", "index.js"]
Step 3 โ Rebuild and compare image sizes
docker build -t my-docker-app:v2 .
docker images
Look at the SIZE column. Your new image should be noticeably smaller than my-docker-app (the old one).
Step 4 โ Test it still works
docker run -p 3000:3000 my-docker-app:v2
Open http://localhost:3000 โ should still work perfectly.
โ Done when
- Dockerfile uses two
FROMstatements - Final stage uses
node:20-alpine -
docker imagesshows the new image is smaller than the old one - App still responds correctly at
http://localhost:3000
๐ Quiz
Answer all 2 questions correctly to complete this lesson.