Docker Tutorial · Building Images · Lesson 8 of 26

Layer Caching and Fast Builds

Order Dockerfile instructions so dependency installs are cached and code changes rebuild in seconds.

Create a free account to save this page, take notes and track your progress. Sign in

How the cache works

Docker reuses a cached layer until it reaches an instruction whose inputs changed (the instruction text, or the checksums of files a COPY reads). Every instruction after that is rebuilt.

So copy dependency manifests and install dependencies before copying the rest of the source:

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt   # cached until requirements.txt changes
COPY . .                                             # code changes only invalidate from here
Only the steps after the change are rebuilt
# a throwaway builder gives this demo its own empty build cache
docker buildx create --name rc-cache-demo --driver docker-container > /dev/null
trap 'docker buildx rm -f rc-cache-demo > /dev/null 2>&1; docker image rm -f rc-cache:1 > /dev/null 2>&1' EXIT
echo "requests==2.32.5" > requirements.txt
echo "print('v1')" > app.py
cat > Dockerfile <<'EOF'
FROM python:3.14-slim
WORKDIR /app
COPY requirements.txt .
RUN echo "installing dependencies (slow step)" && sleep 2
COPY app.py .
CMD ["python", "app.py"]
EOF
# list each step as "cached" or "built" from BuildKit's plain progress output
build() {
  docker buildx build --builder rc-cache-demo --load --progress=plain -t rc-cache:1 . 2>&1 |
    awk '/^#[0-9]+ \[[0-9]\/[0-9]\] (RUN|COPY|WORKDIR)/ {n=$1; sub(/^#[0-9]+ /, ""); step[n]=$0}
         /^#[0-9]+ CACHED/ {cached[$1]=1}
         END {for (n in step) print step[n] "  ->  " (n in cached ? "cached" : "built")}' | sort
}
echo "--- first build"; build
echo "print('v2')" > app.py
echo "--- after editing app.py"; build
docker run --rm rc-cache:1

Output

--- first build
[2/5] WORKDIR /app  ->  built
[3/5] COPY requirements.txt .  ->  built
[4/5] RUN echo "installing dependencies (slow step)" && sleep 2  ->  built
[5/5] COPY app.py .  ->  built
--- after editing app.py
[2/5] WORKDIR /app  ->  cached
[3/5] COPY requirements.txt .  ->  cached
[4/5] RUN echo "installing dependencies (slow step)" && sleep 2  ->  cached
[5/5] COPY app.py .  ->  built
v2

Run this on your computer — see “Get Started” for setup.

BuildKit also offers cache mounts (RUN --mount=type=cache,target=/root/.cache/pip ...) and remote caches for CI. See Docker build cache.

Test yourself

Why copy requirements.txt before the rest of the source code?

  1. It makes images smaller
  2. So the dependency install layer stays cached when only code changes
  3. Docker requires it
  4. To hide the source
Show answer

Answer: So the dependency install layer stays cached when only code changes

Any change to copied files invalidates that layer and everything after it.

Was this page helpful?