Optimizing Build Cache¶
Docker builds each instruction as a layer and reuses a cached layer while its inputs and parent layers are unchanged. Order instructions so expensive, stable work stays cached and only the final steps rerun.
Instruction Ordering¶
- Put rarely changing instructions first and frequently changing instructions last.
- Install dependencies before copying application source:
- Copy the dependency manifest and lock file.
- Install dependencies.
- Copy the source.
- Set
ENVvalues that change per commit late in the file; every subsequent layer is invalidated when they change. - Keep
.dockerignorecurrent. An unrelated file in the context invalidatesCOPY . ..
Node.js example:
Combine Related Commands¶
- Each
RUNcreates a layer. Combine commands that depend on each other into oneRUN. apt-get updateandapt-get installMUST share oneRUN. A cachedupdatelayer with a stale package index makesinstallfail.- Cleanup MUST happen in the same
RUNthat created the files:
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
BuildKit Cache Mounts¶
Cache mounts persist package manager caches across builds.
- Use
sharing=lockedfor package managers that must not run concurrently. - Cache mounts do not appear in the final image, so they do not inflate its size. When a cache mount is used, the manual cache cleanup in the same
RUNbecomes unnecessary. - Go builds cache the compiler and module downloads separately:
--mount=type=cache,target=/root/.cache/go-buildand--mount=type=cache,target=/go/pkg/mod.
CI Cache Export¶
For CI, export and import cache with --cache-to and --cache-from, for example type=registry or type=gha. Without this, every CI build starts with an empty cache.
Debugging Cache Behavior¶
- Inspect layer sizes with
docker history <image>. - Build with
docker buildx build --progress=plainto see which steps rerun. - If a layer rebuilds unexpectedly, check what changed in the build context, the
ARG/ENVvalues the layer consumes, and its parent layers.