Speed Up Docker Builds 10x with Cache Mounts
Your Docker build reinstalls every dependency from scratch whenever package.json, requirements.txt, or composer.json changes by even one line. Layer caching is all-or-nothing — one changed dependency means re-downloading everything.
BuildKit cache mounts fix this. They persist a directory across builds so your package manager keeps its download cache, even when the layer rebuilds.
The Syntax
RUN --mount=type=cache,target=/path/to/cache \
your-install-command
That's it. BuildKit mounts a persistent directory at the target path during the build step. It survives layer invalidation.
Real Examples
Node.js (npm)
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
Python (pip)
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
COPY . .
PHP (Composer)
FROM php:8.4-fpm
WORKDIR /app
COPY composer.json composer.lock ./
RUN --mount=type=cache,target=/root/.composer/cache \
composer install --no-dev --no-scripts
COPY . .
System packages (apt)
RUN --mount=type=cache,target=/var/cache/apt \
--mount=type=cache,target=/var/lib/apt/lists \
apt-get update && apt-get install -y \
libpng-dev \
libzip-dev
Why This Is Faster Than Layer Caching
Standard layer caching works when your lockfile hasn't changed. But the moment you add or update a single package, Docker throws away the entire cached layer and downloads everything again.
Cache mounts keep the downloaded packages on disk. When you add one new dependency, your package manager only downloads that one package — the rest are already in the cache. The difference on a project with 200+ dependencies is dramatic: from minutes to seconds.
CI/CD Gotcha
Cache mounts are local to the build host. In CI, each run typically starts fresh, so the cache is empty. To fix this:
- GitHub Actions: Use
docker/build-push-actionwithcache-fromandcache-tooptions pointing to the GitHub Actions cache or a registry. - Self-hosted runners: The cache persists automatically between runs.
- Depot / Earthly / Buildkite: These CI tools support persistent BuildKit cache natively.
Takeaway
Add --mount=type=cache,target=<cache-dir> to your RUN install commands. One line change, and your rebuilds go from downloading the internet to a near-instant cache hit. It works today with any Docker version that has BuildKit enabled (default since Docker 23.0).