Course outline · 0% complete

0/29 lessons0%

Course overview →

The build cache and .dockerignore

lesson 3-3 · ~12 min · 9/29

Why builds are fast the second time

Running docker build twice in a row finishes the second run instantly, with CACHED next to every step. Docker reuses the layer from last time whenever it can prove nothing changed.

The cache rule is worth memorizing.

  1. An instruction's layer is reused if the instruction text is unchanged and any files it copies are unchanged.
  2. The moment one step misses the cache, every step after it rebuilds too, because each layer builds on the previous one.
ChangeFirst step to miss
edit server.pythe COPY . . near the bottom
edit requirements.txtthe copy of that file, near the top
edit the FROM lineeverything

Rule 2 is why order matters. Put the things that change rarely, meaning dependencies, near the top, and the things that change constantly, meaning your code, near the bottom.

edited server.py edited requirements.txt FROM python:3.12-slim cached COPY requirements.txt cached RUN pip install cached COPY . . rebuild about 1 second FROM python:3.12-slim cached COPY requirements.txt rebuild RUN pip install rebuild COPY . . rebuild minutes
A cache miss cascades downward. Editing code invalidates only the bottom layers, while editing the dependency file above forces the expensive install to rerun.

The classic pattern, explained

The Dockerfile from lesson 3-1 now makes full sense:

COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

Copying requirements.txt alone first and everything later is a deliberate choice driven by what changes between builds.

Editing server.py and rebuilding leaves requirements.txt unchanged, so its copy and the whole pip install layer are cache hits, and only the final COPY . . reruns. Build time is about a second.

If the Dockerfile instead said COPY . . before pip install, any code edit would invalidate that copy, and rule 2 would force pip to reinstall every package on every build. Build time becomes minutes, every time.

OrderingRebuild after a code edit
dependencies firstone cheap copy layer
code firsta full reinstall

The resulting image is identical either way. The difference is entirely in the feedback loop, which is what determines whether developers rebuild freely or avoid it.

A cache oracle in bash

A small script playing the role of Docker deciding where a rebuild starts, based on which file changed.

changed="server.py"
case "$changed" in
  requirements.txt) echo "first miss: COPY requirements.txt (pip install reruns too)" ;;
  *) echo "first miss: COPY . . (pip install layer reused)" ;;
esac

Output

first miss: COPY . . (pip install layer reused)

server.py changed here, so the expensive pip install layer is safely reused. Setting changed to requirements.txt takes the other branch, where the install reruns because a layer above it missed.

The case form is bash's pattern match, with *) as the catch-all and ;; ending each branch. It models the real cache decision closely, because Docker also stops at the first miss and rebuilds everything below it.

Which steps rerun after a code edit

For a Dockerfile of FROM, COPY requirements.txt, RUN pip install, COPY ., and CMD, editing only server.py reruns just COPY . . and the cheap CMD metadata step after it.

requirements.txt did not change, so its copy layer is a cache hit and so is the pip install layer built on top of it. The first miss is COPY . ., and everything after that point reruns.

StepStatus
FROMcached
COPY requirements.txtcached
RUN pip installcached
COPY . .rebuilt
CMDrebuilt, and free

This is exactly why dependencies are copied and installed before the code. The expensive step sits above the line where changes happen, so it is almost never invalidated.

.dockerignore

COPY . . copies the whole build context, which can drag in junk: the .git history, node_modules/, virtualenvs, and .env files holding secrets. That bloats the image, slows the build, and can leak credentials.

The fix is a .dockerignore file next to your Dockerfile, the same idea as the .gitignore from the git course:

.git
node_modules
__pycache__
.env
*.log
EntryKeeps out
.gitthe entire version history
node_modulesreinstallable dependencies
.envsecrets
*.lognoise, matched by pattern

Anything listed is excluded from the build context, so COPY . . never sees it. Every real project should have one, and .env belongs in it for exactly the reason it belongs in .gitignore. Secrets get a proper home in lesson 8-2.

The file that trims the build context

The file is .dockerignore.

It works like .gitignore but for docker build, and the name starts with a dot and ends with ignore. It keeps .git, dependency folders, caches, and secret files such as .env out of the build context, so COPY cannot pull them into the image.

BenefitWhy
smaller imagesfewer bytes in the copy layer
faster buildsless context to send to the daemon
no leaked secrets.env never reaches the image

The last benefit is the one people underestimate. A secret baked into a layer stays in the image history even if a later instruction deletes the file, so keeping it out of the context in the first place is the only real fix.