Your Docker builds are slow. This one line fixes it.

Share

Stop re-downloading the internet on every build

If your Docker builds take 3+ minutes because npm ci or pip install re-fetches every dependency each time your code changes, you're leaving massive time on the table. There's a BuildKit feature that fixes this, and almost nobody uses it.

The problem

You probably do something like this: copy package.json, run npm ci, then copy source. Smart — Docker caches the dependency layer. But the moment you touch package.json (even a version bump in devDependencies), that layer busts and you re-download everything.

The fix: cache mounts

BuildKit lets you mount a persistent cache directory that lives outside the layer system. It never invalidates. It just quietly holds your downloaded packages across every build, local and CI.

# syntax=docker/dockerfile:1
FROM node:20
WORKDIR /app

RUN --mount=type=cache,target=/root/.npm \
    npm ci

COPY . .

That --mount=type=cache line is the magic. The npm cache persists in BuildKit's storage. Next build? npm ci flies because everything's already there — even after a package.json change.

Same pattern works everywhere:

  • Python: --mount=type=cache,target=/root/.cache/pip
  • Rust: --mount=type=cache,target=/usr/local/cargo/registry
  • Go: --mount=type=cache,target=/root/.cache/go-build
  • Apt: --mount=type=cache,target=/var/cache/apt

BuildKit is default in Docker Engine 23+ and Docker Desktop. In CI, just set DOCKER_BUILDKIT=1 if you're on something older. On GitHub Actions, the docker/build-push-action handles this automatically.

TL;DR

  • Use --mount=type=cache in your Dockerfile to persist package caches across builds
  • No layer invalidation — your dependency cache survives code and package.json changes
  • Works with npm, pip, cargo, go, apt — anything that caches downloads locally