Speed up Docker builds with layer caching
Slow Docker builds delay every pull request. Order your Dockerfile for the layer cache, shrink the build context and use BuildKit cache mounts to cut build time.
A Docker build that takes eight minutes is eight minutes added to every pull request and every deploy. Most of that time is usually spent reinstalling dependencies that did not change. Docker can skip that work if the Dockerfile lets it.
How the layer cache works
Docker builds an image one instruction at a time and caches the result of each one as a layer. On the next build it reuses a cached layer as long as the instruction and the files it depends on are unchanged. The moment one layer changes, every layer after it is rebuilt.
So the rule is simple: put what changes rarely at the top and what changes often at the bottom.
Copy the dependency manifest first
This Dockerfile reinstalls every package whenever any source file changes:
COPY . .
RUN npm ci
This one reinstalls only when the dependencies change:
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
It is the single most valuable change for build speed, and it works the same way for requirements.txt, go.mod or composer.json.
Keep the build context small
Before the build starts, Docker sends the whole folder to the builder. A .dockerignore file keeps large and irrelevant paths out.
node_modules
.git
dist
coverage
A smaller context uploads faster and stops unrelated file changes from invalidating the cache.
Use BuildKit cache mounts
BuildKit, the default builder in current Docker versions, can keep a package manager's cache between builds, even when the layer itself has to be rebuilt.
RUN --mount=type=cache,target=/root/.npm npm ci
When a dependency changes, npm downloads only the new package instead of all of them.
Share the cache in CI
A fresh CI runner starts with an empty cache. Export it to your registry and import it on the next run.
docker buildx build \
--cache-from type=registry,ref=registry.example.com/app:cache \
--cache-to type=registry,ref=registry.example.com/app:cache,mode=max \
-t registry.example.com/app:latest --push .
What to expect
With the manifest copied first and a shared cache, a build where only application code changed often drops from minutes to well under one. Faster builds mean faster feedback, and less paid CI time.
If the images themselves are heavy, the next step is to reduce Docker image size with multi-stage builds.