E-NO
Docker Build Cache commands 10 Min Read

Docker Build Cache Commands: A Practical Hands-On Guide for Developers

calendar_today Published: 2026-10-17
update Last Updated: 2026-10-17
analytics SEO Efficiency: 100%
Technical guide illustration for Docker Build Cache Commands: A Practical Hands-On Guide for Developers.

Intro

Docker's build cache is one of the most powerful features for speeding up image builds, but it is also one of the most misunderstood. When it works, developers save minutes on every build. When it silently misses, every build starts from scratch and CI pipelines become painfully slow. This guide is for developers, DevOps consultants, and technical startup teams who want to stop guessing and start verifying exactly how the build cache behaves in their own environment.

We will focus on operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify every result, and document how to recover when an expected state is not reached. You will learn the basic commands for inspecting and manipulating the build cache, see practical examples with expected output, and get a cheat sheet of the most useful checks. We will also cover related concepts such as BuildKit, Dockerfile structure, and layer caching, but only where they directly affect cache behavior, troubleshooting, or recovery.

By the end of this article, you will be able to run a small production-like local test, interpret its results, and apply the same commands confidently in a team environment. All examples use plain Docker CLI commands that work on Linux, macOS, and Windows with Docker Desktop.

Version and Environment Inventory

Before running any cache-related commands, confirm the version of Docker and BuildKit you are using. Cache behavior has changed significantly between legacy builder and BuildKit, and some flags or outputs differ across versions. Run the following read-only commands to gather the current state:

docker version --format '{{.Server.Version}}'
docker buildx version

Example output on a typical Docker Desktop installation:

24.0.7
github.com/docker/buildx v0.12.1

Also check which builder is active. BuildKit was enabled by default in Docker 23.0 and later, but it is worth confirming:

docker buildx ls

Expected output includes a line with default * indicating the selected builder. If you still see legacy or no asterisk next to the default builder, consider running docker buildx install to set BuildKit as the standard builder.

For Compose projects, use docker compose version and docker compose config --images to list the images referenced by your services. This gives you a quick inventory before you touch any build commands.

Data Storage and Cache Location

The build cache is stored inside the Docker daemon's data directory, typically under /var/lib/docker on Linux and inside the Docker Desktop VM on macOS/Windows. If you are using the legacy builder, cache is managed inside the Docker overlay filesystem; with BuildKit, cache is stored separately and can even be exported to a remote registry. You can inspect disk usage with:

docker system df

Example output:

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          12        2         3.45GB    1.87GB (54%)
Containers      3         1         45MB      0B (0%)
Local Volumes   5         2         2.1GB     900MB (42%)
Build Cache     48        0         5.2GB     5.2GB (100%)

Notice the Build Cache line. If the reclaimable size is large and you never need those intermediate layers again, you can clear it with docker builder prune, but hold off until you understand what you might lose. We will cover pruning safely later.

For data inside containers, confirm where files are stored before changing containers. A named volume such as app_data:/var/lib/app is managed by Docker and survives container re-creation. A bind mount such as ./data:/var/lib/app maps directly to a host directory and is easier to back up but can cause permission or portability issues across machines. A restart test is the quickest way to verify persistence: stop the container, recreate it, and check whether the expected files are still present. If they disappear, the service was likely writing to the container's ephemeral filesystem instead of the mounted volume.

Quick check 1 of 2

According to the reference passage, what happens to downstream layers when a layer changes in a Docker build?

The passage states: 'If a layer changes, all other layers that come after it are also affected. When the layer with the COPY command gets invalidated, all layers that follow will need to run again, too.'

Safe Configuration Path

When you change build cache settings, do it in the smallest safe step. The most common configuration change is switching between legacy builder and BuildKit. Since Docker 23, BuildKit is default, but many teams still have DOCKER_BUILDKIT=0 in their environment for legacy reasons. Set the environment variable per command rather than globally:

# One-time BuildKit build
DOCKER_BUILDKIT=1 docker build -t myapp:test .

# Back to legacy for this command only
DOCKER_BUILDKIT=0 docker build -t myapp:test-legacy .

To make BuildKit the default for all future shell sessions, add export DOCKER_BUILDKIT=1 to your shell profile, but be aware that some older Dockerfiles with unusual syntax or certain plugins may behave differently. Verify which builder is active after the change:

docker buildx version && docker buildx ls

If you use the legacy builder, the cache is stored inside the Docker daemon and cannot be easily exported. With BuildKit, you can specify a cache storage location using the --cache-from and --cache-to flags, which is especially useful in CI. Example for pulling and pushing cache to a registry:

docker buildx build --cache-from type=registry,ref=myregistry/myapp:cache --cache-to type=registry,ref=myregistry/myapp:cache,mode=max -t myapp:latest .

This tells BuildKit to look for cache layers in the registry image myapp:cache before building, and to push new cache layers after a successful build. The mode=max includes all intermediate layers, not just the final image. For local development, a simpler approach is to rely on the local cache and only avoid --no-cache unless you genuinely need a fully reproducible build.

Configuration File Options

If you prefer a declarative approach, you can set builder options in /etc/docker/daemon.json (Linux) or Docker Desktop settings. For example, to enable BuildKit and set a max cache size, add:

{
  "features": {
    "buildkit": true
  },
  "builder": {
    "gc": {
      "enabled": true,
      "defaultKeepStorage": "10GB"
    }
  }
}

After changing daemon.json, restart the Docker daemon (sudo systemctl restart docker on Linux) and confirm with docker info | grep -i buildkit. For Docker Desktop, you can change these settings in the GUI under Preferences > Docker Engine, then click Apply & Restart.

Always document the exact change and how to revert it. A good practice is to keep configuration files in version control and use a pull request for any change that affects shared build infrastructure.

Verification and Diagnostics

Now that you know your environment, let us verify how the cache is actually working. The most direct way is to run a build twice and observe the output. Create a simple Dockerfile:

FROM alpine:3.19
RUN apk add --no-cache curl
COPY app.sh /app.sh
RUN chmod +x /app.sh
CMD ["/app.sh"]

Run the build twice and compare timing and cache hits:

time docker build -t myapp:test .
time docker build -t myapp:test .

On the second build, with a warm cache, you should see lines like:

=> CACHED [1/4] FROM docker.io/library/alpine:3.19
=> CACHED [2/4] RUN apk add --no-cache curl
=> CACHED [3/4] COPY app.sh /app.sh
=> CACHED [4/4] RUN chmod +x /app.sh
=> exporting to image
=> => exporting layers
=> => writing image sha256:...

The CACHED marker confirms the layer was reused. If you do not see CACHED, the cache missed for that layer, which often means the layer's inputs changed or there is a problem with the cache mechanism.

Detailed Cache Inspection with BuildKit

BuildKit provides a more detailed view of cache usage. Use docker buildx build --progress=plain to see the full log, including cache decisions. For an even deeper look, you can use docker buildx du to see disk usage by cache type:

docker buildx du

Output may resemble:

Total:     2.08GB
Reclaimable: 1.05GB
Shared:    300MB
Private:   1.78GB

To see which Dockerfile lines are causing cache misses, rebuild with a small change and watch the log. For example, if you modify app.sh, only the last three layers rebuild; the RUN apk add layer stays cached. If the RUN apk layer rebuilds even when you did not change that line, then something earlier in the Dockerfile or build context changed, such as file timestamps in the build context.

To see the exact cache key for each instruction, run with verbose output:

docker buildx build --progress=plain --no-cache=false -t myapp:test . 2>&1 | grep -A3 'RUN apk'

The --no-cache=false is the default but explicit. Look for the CacheKey lines in the output. They show the hash of the inputs used to look up cache. If the hash changes, the cache misses.

Using docker image history

For the legacy builder, you can inspect the layers of an existing image to see if a particular layer is likely to be reused. Run:

docker image history myapp:test

Example output:

IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
0123456789ab   2 minutes ago    CMD ["/app.sh"]                                0B        buildkit.dockerfile.v0
<missing>      2 minutes ago    RUN /bin/sh -c chmod +x /app.sh # buildkit     33B       buildkit.dockerfile.v0
<missing>      2 minutes ago    COPY app.sh /app.sh # buildkit                 45B       buildkit.dockerfile.v0
<missing>      2 minutes ago    RUN /bin/sh -c apk add --no-cache curl # bui…   5.5MB     buildkit.dockerfile.v0
<missing>      2 weeks ago      /bin/sh -c #(nop)  CMD ["/bin/sh"]            0B
<missing>      2 weeks ago      /bin/sh -c #(nop) ADD file:... in /            7.05MB

Layers with <missing> in the IMAGE column are intermediate layers that were not tagged but still exist in the cache. This history helps you understand the order and size of each layer.

Checking Cache in Remote Environments

If you are using a remote builder or CI service, you may need to use docker buildx ls to list builders and docker buildx use <builder> to switch context. To check whether a remote registry contains a cache image, use:

docker manifest inspect myregistry/myapp:cache >/dev/null && echo "Cache image exists" || echo "No cache image"

If the cache image does not exist, the first build will populate it via --cache-to. Subsequent builds should use --cache-from to retrieve it. Always verify that the credentials for the registry are available in the build environment, typically via docker login or CI secrets.

Failure Modes and Recovery

Cache misses are not always benign. They can cause builds to fail, produce unexpected results, or silently consume large amounts of disk space. Here are the most common failure modes and how to recover from them.

Cache Miss Due to Build Context Changes

The entire build context is hashed to determine if a COPY or ADD instruction has changed. If you do COPY . /app, then any change to any file in the current directory (including .git, temporary files, or a large node_modules) will invalidate the cache for that instruction and everything after it. This is the number one reason cache seems to "never work."

Recovery:

  • Use a .dockerignore file to exclude unnecessary files. Example:
  .git
  node_modules
  *.log
  tmp/
  • Be specific in COPY commands. Instead of COPY . /app, copy only the files you need: COPY package.json /app/package.json, then RUN npm install, then COPY src /app/src. This way, changes to src do not invalidate the npm install layer.

Cache Miss Due to Timestamp or Metadata Changes

Even if file contents are identical, changed timestamps or permissions can cause a cache miss because the layer cache key includes metadata. This often happens when files are checked out from version control with current timestamps or when a file is copied from a different filesystem.

Recovery:

  • Use --no-cache only for the problematic instruction? Not directly; instead, sort out the source of metadata changes. For Git checkouts, use git checkout-index -a -f which sets timestamps, or use a build system that normalizes file metadata before build.
  • In BuildKit, you can use RUN --mount=type=cache,target=/root/.cache to mount a persistent cache volume for commands like apt-get, pip, or npm. This cache is independent of file changes in the build context, so it persists even when other layers rebuild.

Cache Corruption or Inconsistent State

Occasionally, the cache itself can become corrupted or contain stale layers from previous builds of different branches or configurations. This can lead to non-deterministic builds or errors like failed to compute cache key or unknown blob.

Recovery:

  • Run docker builder prune to remove all build cache. Be careful: this removes all cache for all projects, not just the current one. A safer approach is to prune only unused cache with docker builder prune --filter until=24h to remove cache older than 24 hours, or use docker buildx prune --filter=label=project=myapp if you label your builds.
  • If corruption persists, restart the Docker daemon. On Linux, sudo systemctl restart docker. On Docker Desktop, use the Restart option in the menu. After restart, run a fresh build with --no-cache once to rebuild from scratch, then subsequent builds should cache normally.

Running Out of Disk Space

Build cache can consume tens of gigabytes, especially with multiple projects and frequent rebuilds. When the disk fills up, Docker may fail to create new layers, containers may not start, and builds will error with no space left on device.

Recovery:

  • Check disk usage with df -h for the Docker data directory.
  • Run docker system df to see how much space is reclaimable.
  • Use docker builder prune with a size filter: docker builder prune --keep-storage=10GB to keep at most 10GB of cache.
  • For a more aggressive cleanup, docker system prune -a removes all unused images, containers, and cache, but will force every image to be rebuilt or re-pulled. Schedule regular pruning or use a cron job to avoid surprises.

Dedicated Cache Volumes for Dependency Managers

A frequent pain point is the cache for package managers like npm, pip, or Maven. By default, those caches are inside the layer that runs npm install or pip install, so any change to package.json or requirements.txt invalidates the entire cache. Using a dedicated cache mount preserves the package cache across builds even when dependencies change:

# syntax=docker/dockerfile:1.4
FROM python:3.12
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt

This mount keeps the pip cache in a separate location managed by BuildKit. When you change requirements.txt, only the pip install step runs, but it can reuse the downloaded wheels from the cache, drastically reducing install time. Similar mounts exist for apt, yum, go, maven, and others. Always check the syntax version at the top of your Dockerfile; the RUN --mount feature requires BuildKit and a recent Dockerfile frontend.

Quick check 2 of 2

What is the purpose of the --cache-to option in docker buildx build?

The passage explains: '--cache-to exports the build cache to the specified location.'

Operations Checklist

Use this checklist before and after each build to ensure cache behavior is understood and consistent.

Pre-build checks:

  • [ ] Confirm Docker version and active builder: docker version and docker buildx ls. Owner: whoever runs the build (e.g., developer or CI admin). Revisit when upgrading Docker.
  • [ ] Ensure .dockerignore is present and excludes unnecessary files. Owner: developer responsible for the Dockerfile. Review whenever the build context changes.
  • [ ] Check disk space: df -h on the Docker data directory. Owner: person managing the build machine or CI runner. Check before each large build or weekly.
  • [ ] Verify that any registry credentials needed for --cache-from or --cache-to are available. Owner: CI infrastructure admin. Verify before first use in CI.

During build:

  • [ ] Run build with progress plain to capture full logs: docker buildx build --progress=plain -t myapp:test .
  • [ ] Note which layers show CACHED and which show RUN. Owner: the builder operator. If unexpected misses occur, diagnose immediately.
  • [ ] If using remote cache, ensure cache image is pulled (--cache-from) and pushed (--cache-to). Owner: CI pipeline designer. Confirm after every build.

Post-build checks:

  • [ ] Check image size and layer count: docker image ls myapp:test and docker image history myapp:test. Owner: developer. Review after any significant Dockerfile change.
  • [ ] Test the image runs correctly: docker run --rm myapp:test. Owner: QA or developer. Do this before merging the Dockerfile change.
  • [ ] Document any cache-related issues or workarounds in the project README or team wiki. Owner: team lead or designated maintainer. Update monthly or after each incident.

Periodic maintenance:

  • [ ] Run docker system df and docker buildx du to monitor cache size. Owner: DevOps engineer or whoever manages CI resources. Do this weekly.
  • [ ] Prune unnecessary cache with docker builder prune --filter until=168h (one week). Owner: same as above. Do this at the same weekly interval.
  • [ ] Review builder configuration in daemon.json or Docker Desktop settings. Owner: infrastructure maintainer. Do this quarterly or after Docker version upgrades.

Every item should have a named owner, not a group, to ensure accountability. For example, in a small team, the developer who owns the Dockerfile is also the one who reviews cache behavior. In larger organizations, a platform engineer might own the builder infrastructure. The frequency of review depends on how often the project changes; a rapidly changing project might need weekly cache checks, while a stable one can use monthly reviews.

Common Pitfalls and How to Avoid Them

Here are recurring mistakes developers make with Docker build cache and concrete ways to avoid them.

Pitfall 1: Using docker build --no-cache as a Shortcut

Why it happens: When a build fails, developers often add --no-cache to avoid cache-related issues. This kills all cache and makes every later build slow.

How to avoid: Only use --no-cache when you truly need a clean build (e.g., for reproducible builds or after a toolchain upgrade). Otherwise, try to fix the actual cause of the cache miss. If you must bypass cache for a single instruction, consider using RUN --mount=type=cache for that instruction or invalidating only the necessary layer with a comment change.

Pitfall 2: Copying the Entire Build Context

Why it happens: COPY . /app is easy, but it includes everything in the directory, often huge files and directories that change frequently.

How to avoid: Use a .dockerignore file and be specific in COPY commands. Split COPY operations to avoid invalidating the entire layer chain.

Pitfall 3: Not Using BuildKit's Cache Mounts

Why it happens: Many developers are unaware of cache mounts or think they are complex. Without them, package manager caches are lost with every dependency change, causing slow builds.

How to avoid: Use the RUN --mount=type=cache syntax. It is supported in modern Docker versions and significantly speeds up dependency installation. Refer to Docker documentation for examples for npm, pip, apt, etc.

Pitfall 4: Misunderstanding Layer Caching with ARG and ENV

Why it happens: Changing a build argument (ARG) or environment variable (ENV) that is used in an early layer invalidates all subsequent layers. Developers may not realize that these variables are part of the cache key.

How to avoid: Place ARG and ENV as late in the Dockerfile as possible, ideally just before the instruction that uses them. If an argument is used only for metadata, consider using LABEL instead.

Pitfall 5: Ignoring Cache in CI Pipelines

Why it happens: Many CI systems start with a clean environment, so the Docker cache is empty every build. Without remote cache, builds are as slow as a no-cache build.

How to avoid: Use --cache-from and --cache-to with a registry. In CI, include these flags in the build step. For GitHub Actions, use a dedicated action like docker/build-push-action that supports cache backends. For GitLab CI, use cache with key and paths to persist the Docker layer cache, or use --cache-from with the previous image.

Pitfall 6: Overlooking Disk Space

Why it happens: Build cache accumulates silently, and developers rarely check disk usage until the build fails.

How to avoid: Implement periodic pruning as part of routine maintenance. Set alerts on disk usage for build machines. Use docker builder prune --keep-storage to cap cache size.

Conclusion

Docker build cache commands are only useful when each recommendation is version-scoped, observable, and reversible where the technology permits. Copying a command without checking prerequisites and expected output is not an operations procedure; it is a gamble. Throughout this guide, we have emphasized the importance of seeing the current state, changing one thing at a time, and verifying the result with concrete commands.

As a next step, choose one low-risk verification for your own project: run a build twice with progress plain, note which layers are cached, and then deliberately change a file and observe which layers rebuild. Record the before and after state, and share the results with your team. This small experiment will build intuition far better than reading more abstract documentation.

A reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. Apply that mindset to Docker build cache, and you will spend less time waiting for builds and more time shipping features.

Related Research

Article Quality Score

Reader usefulness 100%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL