Inline Cache

Inline cache is Docker build cache metadata embedded in the image you push, so pulling that image also supplies the cache hints for the next build.

Inline cache is Docker build cache metadata that BuildKit embeds inside the image it pushes, so the image and the hints needed to reuse its layers travel as one artifact. A later build imports it by pointing --cache-from at that image reference, and any builder able to pull the image can read the cache.

The limit sits in what the embedded metadata can describe. Inline cache records the layers present in the final image, so work done in a stage that was dropped along the way leaves nothing for the next build to match against.

Definition

Inline cache is one of the cache export backends supported by BuildKit, selected with --cache-to type=inline on the build command or cache-to: type=inline in an action input. Where other backends write the cache manifest to a separate object, inline writes it into the configuration blob of the image the build produces (Docker inline cache backend documentation, checked on 2026-08-13).

Three properties follow from that placement.

  1. The build has to produce an image. Inline cache is available with the image and registry exporters, so a build that only writes a tarball or a local directory has nowhere to put the metadata.
  2. There is no second artifact to manage. Retention, storage cost, and access control are whatever the image repository already applies to the tag.
  3. The recorded set is bounded by the image. A layer that never reaches the final image cannot be described by metadata that lives inside it.

What each cache backend stores

BackendWhere the cache manifest is writtenCache modes it supportsExtra artifact to manage
inlineThe configuration blob of the pushed imagemin onlyNone, the image tag is the cache
registryA separate manifest at a cache reference you namemin and maxOne cache reference plus its storage
localA directory on the machine that ran the buildmin and maxA directory that has to outlive the build
ghaThe GitHub Actions cache servicemin and maxA store with its own size limit and eviction

The mode column is where inline cache differs in practice. min mode exports the layers of the resulting image. max mode exports the layers produced by every step, including steps in stages that were used and discarded. Inline cache supports min mode alone, so a build that needs the wider record has to write to one of the other backends (Docker cache backend reference, checked on 2026-08-13).

Reading an inline cache back

The import side uses the registry backend, because the metadata sits inside an image in a registry. A build imports it with --cache-from type=registry,ref=<image>, and BuildKit reads the cache records out of the image configuration before deciding which steps to run. The reference has to be one a previous build pushed with inline export, which in most workflows is the image tag for the default branch.

Two consequences are worth planning around. Overwriting the tag replaces the cache, so a floating reference such as :latest holds whatever the last push to it produced. And a pull request that changes an early instruction misses every step downstream of that change, because the digest chain diverges from the recorded one at the first difference.

The build argument form

Before Buildx flags were common, the same behavior was requested through a build argument: docker build --build-arg BUILDKIT_INLINE_CACHE=1 on an engine running BuildKit. That form still works and produces the same embedded metadata. Contents of RUN --mount=type=cache directories stay on the builder under every backend, so a package manager download directory is absent from an inline export the same way it is absent from a registry export.

Example

This Dockerfile compiles in one stage and ships a second, which is the shape where inline cache behaves differently from a dedicated cache export.

# syntax=docker/dockerfile:1

FROM node:22 AS build
WORKDIR /src
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /src/dist /usr/share/nginx/html

The workflow below builds it on a GitHub Actions runner, pushes the image, and asks for inline cache on the way out. The next run imports the same reference.

name: image
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:latest
          cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:latest
          cache-to: type=inline

One docker push moves the image and its cache metadata together. There is no second reference in the registry, and a developer who pulls :latest on a laptop can build against the same cache with the same --cache-from line.

What the second run hits and what it misses

Assume the first run pushed the image and a later push edits one file under src/.

StepRecorded by inline exportSecond run behavior
COPY package.json package-lock.json ./No, the build stage is discardedRuns again
RUN npm ciNo, the build stage is discardedRuns again, downloading the dependency set
COPY . . and RUN npm run buildNo, the build stage is discardedRuns again
FROM nginx:1.27-alpineYes, the layer is in the final imageMatched from the imported cache
COPY --from=build /src/dist ...Yes, the layer is in the final imageRebuilt, since the copied files changed

The npm ci line is the cost. It belongs to a stage the final image does not contain, and inline metadata has no place to record a layer that the image never carries. Switching the export to cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max records the build stage as well, at the price of a separate cache reference, an import download before the build, and an export upload after it. That trade between what the cache describes and what each run pays to move it is the reason both backends exist.

FAQ

What is the difference between inline cache and a registry cache export?

Inline cache writes the cache manifest into the configuration of the image you push, so the image is the cache. A registry cache export writes a separate manifest to its own reference, which can describe every step of the build including stages that never reach the final image. Inline needs no second reference to manage and describes less of the build.

Why does my multi-stage build still rebuild with inline cache?

Inline cache supports min mode only, and min mode records the layers of the resulting image. A builder stage that compiles a binary and hands one file to a slim runtime stage leaves no record behind, so the compile step runs again on the next build while the runtime layers match.

How do I turn inline cache on in a Docker build?

With Buildx, pass cache-to type=inline on a build that pushes an image, then import it on the next build with cache-from type=registry pointed at that image reference. With docker build on an engine running BuildKit, the equivalent is the build argument BUILDKIT_INLINE_CACHE set to 1.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.