Practice brief

Image Layers Deep Dive Exercises

The Dockerfile from Module 5 works but rebuilds npm install every time any file changes. In Module 6 you fix the layer order so dependency installation is cached, and add a .dockerignore so the build context only contains what the image actually needs.

Build from scratch

6.1 Build from scratch
~25-35 min

Optimise the Dockerfile for Fast Rebuilds

The Dockerfile from Module 5 works, but every rebuild installs all npm packages from scratch — even when you only changed a single line of application code. As the project grows this will become a serious time cost. In this exercise you will fix the COPY order so Docker caches the dependency install step, and add a .dockerignore so the build context does not send unnecessary files to the builder. After this, changing a line in the API code will rebuild in seconds.

Try it yourself first. Open the guided path if you get blocked.

Step 1 — See the slow rebuild

echo '// test change' >> api/server.mjs
docker build -t taskflow-api .

Watch the output. Every step after the first COPY rebuilds — including npm install. This is the problem: Docker cannot tell that the packages did not change because you copied all files in one COPY instruction before installing dependencies.

Step 2 — Fix the COPY order in your Dockerfile

Open your Dockerfile. The key change is to copy package.json and package-lock.json before the rest of the code, run npm install, then copy the application files. Docker caches each layer. If package.json has not changed since the last build, the npm install layer is a cache hit and is skipped entirely. COPY package*.json ./ RUN npm install COPY . . If your Dockerfile already has this order from Module 5, it is correct. The next step is what makes the second COPY fast.

Step 3 — Create a .dockerignore file

touch .dockerignore

Without .dockerignore, docker build sends your entire project directory to the builder — including node_modules, which may be hundreds of megabytes. Open .dockerignore and add these entries, one per line. node_modules .env .git .DS_Store node_modules should never go into the image — npm install creates a fresh set inside the container. .env contains secrets that must not be baked into an image. .git adds size with no benefit.

Step 4 — Build again and confirm the cache hit

docker build -t taskflow-api .

Watch the output. The step that runs npm install should now show CACHED. Only the COPY . . step and anything after it will rebuild. This is the difference between a 60-second rebuild and a 3-second one.

Step 5 — Change a line of code and rebuild

echo '// another change' >> api/server.mjs
docker build -t taskflow-api .

Now only the layers after COPY . . rebuild. npm install is cached. The total rebuild time should be a few seconds. This is the correct pattern for any Node.js Dockerfile.

Step 6 — Verify the API still works

docker rm -f taskflow-api
docker run -d --name taskflow-api -p 8000:8000 taskflow-api
curl http://localhost:8000/health

Optimising the layer order does not change the running behaviour — the same image builds and runs the same API. Confirm the health endpoint responds before moving on.

Break-fix

6.2 Break-fix
~15-25 min

The Build That Sends a Gigabyte to the Builder

A teammate cloned the repo and ran docker build -t taskflow-api . on their machine. The build eventually finished but the first line printed was: Sending build context to Docker daemon 1.34GB. Their machine has a node_modules directory from running npm install locally. The final image is only 120MB. But the builder received over a gigabyte of data before building a single layer. Reproduce this, find out what is inflating the build context, and fix it so the context is under 5MB.

npm install
rm -f .dockerignore
docker build -t taskflow-api .
Show investigative hints
  • The first line of docker build output reports how much data was sent to the builder before any layer ran. That number is your clue.
  • The build context is everything in the current directory that Docker sends to the daemon. Docker does not decide what to exclude — you do.
  • There is a file whose purpose is to tell Docker what to leave out of the build context. It works the same way as .gitignore.
Show diagnosis and fix rationale

The build context includes the whole current directory unless .dockerignore excludes files from it. A local node_modules directory can be huge, and Docker sends it before any Dockerfile instruction runs. Add a .dockerignore file that excludes node_modules, .env, logs, and other local-only files. The image can still install dependencies from package.json, but the build context stays small.