Docker 04. Why Docker Builds Become Slow and Where Cache Gets Invalidated
Summary
When you build Docker images repeatedly, some builds finish quickly while others feel like they start over from scratch. Most of that difference comes from build cache and cache invalidation rules. Docker reuses a previous layer when an instruction and the files it depends on have not changed, and once a middle layer changes, the layers that follow it are rebuilt too.
The practical conclusion is that, at the beginner stage, perceived build speed depends at least as much on how well your Dockerfile separates rarely changing inputs from frequently changing ones as it does on raw machine power. In particular, placing COPY . . too early often causes expensive dependency-install layers to rebuild far more often than necessary.
Document Information
- Written on: 2026-04-20
- Verification date: 2026-04-21
- Document type: analysis
- Test environment: Windows PowerShell with Docker Desktop for Windows. After restarting Docker Desktop and the
docker-desktopWSL distribution, tests ran on thedesktop-linuxcontext with Linux containers. - Test version: Docker Desktop 4.70.0(224270), Docker CLI/Engine 29.4.0(API 1.54), Docker Compose v5.1.2, Docker Buildx v0.33.0-desktop.1. Docker official docs were checked on 2026-04-20.
- Source grade: official Docker documentation and local reproduced results are used.
- Note: this post covers the basic cache rules and the most important beginner optimization patterns. It does not go deep into remote cache configuration for CI systems.
Problem Definition
Beginners usually hit the same build-speed problems.
- They change one source file, but dependency installation runs again.
- They expect
RUN apt-get updateto automatically fetch newer packages on the next build. - They do not see why
.dockerignorematters for cache behavior. - They miss how directly Dockerfile instruction order affects build time.
This post focuses on how build cache works and which beginner-level structuring habits make the biggest difference.
Verified Facts
- According to the official documentation, a build layer is reused from cache when the instruction and the files it depends on have not changed. Evidence: Optimize cache usage in builds
- According to the official documentation, once a layer changes, the layers that follow it need to be rebuilt as well. Evidence: Optimize cache usage in builds
- According to the official documentation,
COPY,ADD, and bind-mountedRUN --mount=type=bindinstructions participate in cache checks using file metadata. A change inmtimealone does not invalidate the cache. Evidence: Build cache invalidation - According to the official documentation,
RUNcache is not automatically refreshed between builds. The same command string can still hit cache on a later build. Evidence: Build cache invalidation - According to the official documentation, Docker recommends ordering layers carefully, keeping the context small, and using bind mounts, cache mounts, and external cache when appropriate. Evidence: Optimize cache usage in builds
- According to the official documentation, placing a
.dockerignorefile at the root of the context lets you exclude unnecessary files and directories from the build context. Evidence: Build context
The official docs reduce the “bad order versus better order” pattern to examples like these:
# cache-breaking pattern
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
# easier cache reuse for dependency layers
FROM node:20
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
And a minimal .dockerignore example looks like this:
node_modules
tmp*
Directly Reproduced Results
- Directly confirmed result: on 2026-04-21, I built the same Dockerfile repeatedly from Windows PowerShell, then changed only
app.txtand checked which steps were reused from cache.
docker build --progress=plain -t codex/cache-test:0.2.0 <temp-context>
docker build --progress=plain -t codex/cache-test:0.2.0 <temp-context>
# change only app.txt
docker build --progress=plain -t codex/cache-test:0.2.1 <temp-context>
docker run --rm codex/cache-test:0.2.1
- Result summary: the second unchanged build showed 5
CACHEDsteps. After changing onlyapp.txt, the build reusedCOPY package.txt .andRUN cp package.txt package.copy, whileCOPY app.txt .andRUN cp app.txt app.copyran again. Running the final image printed the updatedapp-v2-...value.
Interpretation / Opinion
My view is that the most common beginner cause of slow Docker builds is not a lack of advanced optimization but copying frequently changing files too early. In Node, Python, or Java projects especially, putting COPY . . before dependency installation often means that even a tiny source change forces a costly reinstall step.
Another common misunderstanding is expecting a command like RUN apt-get update to “refresh itself” on the next build. The official docs make clear that RUN cache is not automatically invalidated by changes inside the container filesystem. If the instruction still matches, Docker may reuse the old cached result. That is why people sometimes rebuild and still see old package state.
At the beginner level, the three most valuable rules are probably these:
- copy rarely changing files first
- keep the build context small
- focus first on what breaks cache, not only on the fact that cache exists
By the time you move into CI/CD, external cache and cache mounts become more important. But even before that, fixing Dockerfile order alone often produces the most noticeable improvement.
Limits and Exceptions
This is not a benchmark post with measured timings from repeated builds across real projects. It does not claim that one structure is always “N times faster.”
Also, cache mounts, external cache, and registry cache depend on the builder and CI environment you use. This post stays with the concepts and beginner-level optimization patterns explicitly documented by Docker.
The direct reproduction only observed cache reuse with a small temporary Dockerfile. I did not benchmark real project build times, remote cache, registry cache, or cache mount performance. This post therefore does not make quantitative speedup claims.
References
- Docker Docs, Optimize cache usage in builds
- Docker Docs, Build cache invalidation
- Docker Docs, Build context
댓글남기기