CD1: Containers done well

Lesson 2 of 5 · reading 15–25 minutes; practice and project time is on the module page

A Dockerfile that builds small and fast

🎯You'll be able to

Write a Dockerfile that uses a pinned, slim base image, orders layers so rebuilds are quick, leaves junk out of the build and uses stages to keep build tools out of the final image.

🏠The everyday picture

When you pack for a trip, you put the things that never change (the toiletry bag) in first and the day's clothes on top, so repacking each morning only touches the top. And you leave the iron at home: you needed it before leaving, not on the trip.

📘What it really is

FROM node:22.23.3-alpine WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY package.json server.js ./ ``` For apps that compile (TypeScript, Go, Java), the first stage holds the compiler and the final image holds only the output.

📝A worked example

Input: replace the starter's Dockerfile with the two-stage version above (plus the lines from Lessons 3 and 4), add app/.dockerignore, and run docker build -t routerani:1.0.0 app twice, changing one line of server.js between builds.

Expected output: docker images routerani shows about 240 MB instead of about 1.6 GB (measured on 1 November 2026). The second build shows CACHED for the npm ci step and finishes in a few seconds. docker run --rm --entrypoint ls routerani:1.0.0 /app lists node_modules, package.json and server.js, and no test folder.

🌍Where it's used

📝Case scenario

An exam-registration site's image was 2.1 GB because it copied the whole project folder, including test videos. On the last day for registration, traffic doubled and new copies took four minutes each to pull the image, so users saw errors while capacity caught up. After a .dockerignore, a slim base and two stages, the image was 180 MB (illustrative figures) and new copies served traffic in under 30 seconds.

🖱️Try it

Build the starter image, then your improved one, and compare docker images sizes and docker history for each. Change one line of server.js and rebuild both. Success: you can point to the line in each Dockerfile that decides whether npm runs again.

⚠️Common mistakes

🛠️For your project

Project 1 test 3 checks that app/.dockerignore leaves out node_modules and that there's no test folder in the image.

✅Quick check

Why copy package-lock.json and run npm ci before copying the code?

Show the answer

So code changes reuse the cached dependency layer and rebuilds are fast.

What does a multi-stage build leave out of the final image?

Show the answer

Whatever only the build needed: compilers, build tools, development packages.

← PreviousImages, containers and layers
ProLearners Assist