How to Fix the “failed to compute cache key” Docker Build Error
Docker builds are usually deterministic: given the same Dockerfile and build context, Docker should compute the same cache keys and reuse layers. When Docker can’t even calculate the cache key for a step, it stops early with an error like:
failed to compute cache key: ...failed to compute cache key: failed to walk ...failed to compute cache key: failed to calculate checksum of ref ...failed to compute cache key: "/path": not foundfailed to compute cache key: failed to read dockerfile: ...
This tutorial explains what the error actually means, how Docker’s cache keys are computed, and a systematic way to diagnose and fix the most common root causes—using real commands and reproducible examples.
Table of Contents
- What the error means
- How Docker computes cache keys (BuildKit)
- Quick triage checklist
- Common causes and fixes
- 1) Missing files in the build context
- 2)
.dockerignoreexcluding required files - 3) Wrong build context (building from the wrong directory)
- 4)
COPY/ADDpath mistakes and glob surprises - 5) Case sensitivity differences (Windows/macOS vs Linux)
- 6) Broken symlinks in the build context
- 7) Permission problems (especially on Linux)
- 8) BuildKit
--mount=type=bindreferencing missing paths - 9)
--mount=type=secret/--mount=type=sshmisconfiguration - 10) Remote contexts and Git submodules not present
- 11) Dockerfile location and
-fconfusion - 12) Corrupted BuildKit state / builder issues
- A step-by-step debugging workflow
- Best practices to prevent cache-key failures
- Reference: useful commands
What the error means
A Docker build step can be cached only if Docker can compute a cache key for it. The cache key is derived from:
- The instruction itself (
RUN,COPY, etc.) - The content of files it depends on (for
COPY/ADD, that includes checksums of source files) - Build arguments and environment affecting the step
- For BuildKit features (
RUN --mount=...), the mount sources and metadata
If Docker says “failed to compute cache key”, it typically means:
- Docker tried to enumerate files used by a step (often a
COPY), but - It couldn’t read them, find them, or traverse them, so
- It couldn’t hash them, so
- It couldn’t decide whether the step can be cached, so the build aborts.
This is different from errors that happen during execution (like RUN npm ci failing). Cache-key failures happen before a step runs.
How Docker computes cache keys (BuildKit)
Modern Docker builds use BuildKit by default in most installations. You can verify:
docker version
docker buildx version
BuildKit changes the build pipeline in important ways:
- It builds a dependency graph (LLB) and computes checksums for inputs.
- For
COPY/ADD, it walks the build context and hashes file contents and metadata. - It can parallelize steps, and it can fail early when an input is missing.
When you see errors like:
failed to calculate checksum of ref ...failed to walk ...lstat /var/lib/docker/...: no such file or directory
that’s BuildKit complaining while trying to compute the digest of input files.
You can explicitly toggle BuildKit to compare behavior:
# BuildKit enabled (common default)
DOCKER_BUILDKIT=1 docker build -t test .
# BuildKit disabled (legacy builder)
DOCKER_BUILDKIT=0 docker build -t test .
If disabling BuildKit “fixes” it, don’t stop there—your build is likely relying on undefined behavior or accidentally copying excluded/missing files. The correct fix is to make inputs consistent.
Quick triage checklist
Before diving deep, run these quick checks:
-
Are you in the correct directory?
pwd ls -la -
Are you using the right build context?
docker build -t myimg . # The dot matters: it defines the context. -
Print the exact failing step
docker build --progress=plain -t myimg . -
Search for the referenced path in the Dockerfile
grep -nE 'COPY|ADD|--mount' Dockerfile -
Check
.dockerignorecat .dockerignore -
Confirm the file exists in the context
ls -la path/from/error
Common causes and fixes
1) Missing files in the build context
Symptom
You see something like:
failed to compute cache key: "/app/package.json": not foundfailed to compute cache key: failed to calculate checksum of ref ... "/src": not found
Why it happens
COPY and ADD can only use files that are inside the build context. The build context is the final argument to docker build (often .). Docker sends that directory (minus .dockerignore exclusions) to the builder.
If your Dockerfile says:
COPY package.json package-lock.json ./
but package-lock.json is not present in the context directory, BuildKit fails while computing the checksum.
Fix
Ensure the file exists and is spelled correctly:
ls -la package.json package-lock.json
If the file is generated (e.g., by CI), generate it before building:
npm install --package-lock-only
docker build -t myapp .
If the file is in a subdirectory, adjust COPY paths or context:
# If Dockerfile is at repo root but files are in ./frontend
COPY frontend/package.json frontend/package-lock.json ./
Or build with a different context:
docker build -t myapp -f frontend/Dockerfile frontend
2) .dockerignore excluding required files
Symptom
The file exists on disk, but Docker still says it’s not found or fails checksum computation.
Why it happens
Docker applies .dockerignore patterns to the context before sending it to the builder. If you accidentally ignore a file, it will not exist inside the build context, even though it exists locally.
A common mistake is ignoring too broadly:
# .dockerignore (problematic)
node_modules
*.json
Now package.json is excluded.
Fix
Inspect what you ignore:
cat .dockerignore
Remove or narrow patterns. Example:
node_modules
npm-debug.log
.git
Dockerfile*
If you need to ignore many files but keep a specific one, use negation:
*.json
!package.json
!package-lock.json
Then rebuild:
docker build --progress=plain -t myapp .
3) Wrong build context (building from the wrong directory)
Symptom
Everything looks correct, but Docker can’t find files referenced by COPY.
Why it happens
You might be running docker build from a directory that doesn’t contain the files you expect. The build context is not “where the Dockerfile is”—it’s the path you pass as the last argument.
Fix
Be explicit about both Dockerfile and context:
# Dockerfile is in ./docker/Dockerfile, but context must be repo root
docker build -f docker/Dockerfile -t myapp .
If your context should be a subfolder:
docker build -f Dockerfile -t myapp ./frontend
To confirm what Docker is sending, look for the “transferring context” line:
docker build --progress=plain -t myapp .
If it says transferring context: 2B, you’re probably ignoring almost everything or pointing at an empty directory.
4) COPY/ADD path mistakes and glob surprises
Symptom
Errors involving globs or directories:
failed to compute cache key: failed to walk ...lstat ... no such file or directory
Why it happens
COPY supports limited wildcard behavior, but it’s easy to write patterns that match nothing, especially when .dockerignore removes files. BuildKit then can’t compute checksums for expected inputs.
Example:
COPY dist/*.js /app/dist/
If dist/ doesn’t exist yet (because it’s built later) or is ignored, the pattern matches nothing.
Fix options
- Ensure the files are generated before
docker build(build artifacts on host), or - Generate them inside the image using multi-stage builds.
Preferred: multi-stage build
# Stage 1: build
FROM node:20 AS build
WORKDIR /src
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: runtime
FROM nginx:alpine
COPY --from=build /src/dist/ /usr/share/nginx/html/
Build:
docker build --progress=plain -t mysite .
This avoids relying on host-side dist/ existing.
5) Case sensitivity differences (Windows/macOS vs Linux)
Symptom
Build works on one machine but fails on another with missing file errors.
Why it happens
Windows and default macOS filesystems are often case-insensitive, while Linux is case-sensitive. You might have:
- File:
Config.json - Dockerfile:
COPY config.json /app/config.json
On a case-insensitive filesystem, this may appear to work locally but fail in Linux-based CI or remote builders.
Fix
Normalize naming and update Dockerfile references:
# On macOS/Linux, rename safely:
git mv Config.json config.json
Then rebuild.
6) Broken symlinks in the build context
Symptom
Errors like:
failed to compute cache key: failed to walk ...lstat ...: no such file or directory
Why it happens
If your context contains symlinks pointing to files that don’t exist (or exist outside the context in a way the builder can’t resolve), BuildKit may fail while walking the directory tree and hashing.
Fix
Find broken symlinks:
# From the context root
find . -xtype l -print
Either remove them, fix the link targets, or exclude them via .dockerignore:
path/to/broken-link
Then rebuild.
7) Permission problems (especially on Linux)
Symptom
Errors mentioning permission denied while walking files:
failed to compute cache key: failed to walk ...: permission denied
Why it happens
BuildKit needs to read files to hash them. If your context includes directories/files that your user can’t read (or that Docker can’t read due to ACLs), it can’t compute checksums.
Common culprits:
- Root-owned files in the repo
- Private SSH keys with strict permissions (also a security issue)
- Mounted directories with restrictive permissions
Fix
Check permissions:
namei -l path/from/error
ls -la path/from/error
Fix ownership/permissions (example):
sudo chown -R "$USER":"$USER" .
chmod -R u+rwX .
If the files should not be in the build context at all (e.g., secrets), exclude them:
id_rsa
*.pem
.env
Then rebuild.
8) BuildKit --mount=type=bind referencing missing paths
Symptom
You use BuildKit syntax like:
RUN --mount=type=bind,source=.,target=/src \
make build
and get cache-key failures or checksum errors.
Why it happens
A bind mount in BuildKit still relies on the build context (or other sources). If source=... points to a path that doesn’t exist in the build context, BuildKit can’t compute the key for that RUN step.
Fix
- Ensure the
sourcepath exists within the context. - Prefer
COPYfor deterministic builds unless you specifically need bind mounts.
Example correction:
WORKDIR /src
COPY . .
RUN make build
If you need --mount=type=cache (safe and common), that’s different and usually not the cause:
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
9) --mount=type=secret / --mount=type=ssh misconfiguration
Symptom
Errors occur around steps using secrets/ssh:
failed to compute cache key: ...- or BuildKit complains about missing secret/ssh configuration.
Why it happens
Secrets and SSH mounts are not part of the build context, but they affect how BuildKit computes the build graph. If you reference a secret that isn’t provided at build time, BuildKit can fail.
Fix: secrets
Dockerfile:
# syntax=docker/dockerfile:1.6
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm ci
Build command must provide the secret:
docker build --secret id=npmrc,src=$HOME/.npmrc -t myapp .
Fix: ssh
Dockerfile:
# syntax=docker/dockerfile:1.6
RUN --mount=type=ssh git clone git@github.com:org/private-repo.git
Build with SSH forwarding:
docker build --ssh default -t myapp .
Also ensure your SSH agent is running:
ssh-add -l
10) Remote contexts and Git submodules not present
Symptom
Building from a Git URL or CI checkout fails, but local build works.
Examples:
docker build https://github.com/org/repo.git#main
or CI checks out without submodules, then Dockerfile does:
COPY vendor/somelib /app/vendor/somelib
Why it happens
Remote contexts may not include submodules unless explicitly fetched. CI providers often do shallow clones or omit submodules by default. BuildKit can’t hash what isn’t there.
Fix
If you rely on submodules, ensure they’re present before building.
In CI:
git submodule update --init --recursive
docker build -t myapp .
If using remote Git contexts, consider switching to a local context in CI after checkout, or ensure the remote context includes everything you need.
11) Dockerfile location and -f confusion
Symptom
You specify -f and Dockerfile references paths that don’t exist.
Why it happens
Paths in COPY are always relative to the build context, not the Dockerfile’s directory.
Example structure:
repo/
docker/
Dockerfile
app/
package.json
Dockerfile:
COPY app/package.json /app/
Correct build:
docker build -f docker/Dockerfile -t myapp .
# context is repo root
Incorrect build:
docker build -f docker/Dockerfile -t myapp docker
# context is ./docker, so app/ doesn't exist
Fix
Always choose the context that contains all needed files.
12) Corrupted BuildKit state / builder issues
Symptom
You’ve verified paths and .dockerignore, but builds still fail inconsistently, especially after upgrades or disk issues.
Why it happens
BuildKit stores state (cache, metadata, intermediate refs). Rarely, the builder’s state can get into a bad condition.
Fix options
- Prune build cache:
docker builder prune
More aggressive:
docker builder prune --all --force
- Prune system-wide unused data (careful: this removes unused images/containers/networks):
docker system prune --all
- If using
buildxwith a named builder, recreate it:
docker buildx ls
docker buildx rm mybuilder
docker buildx create --name mybuilder --use
docker buildx inspect --bootstrap
Then rebuild with plain progress:
docker buildx build --progress=plain -t myapp .
A step-by-step debugging workflow
When you hit the error, use this workflow to converge quickly.
Step 1: Get the full error and the failing instruction
docker build --progress=plain -t debugimg .
Look for the step number and instruction, e.g.:
#7 [3/8] COPY package.json package-lock.json ./
#7 ERROR: failed to compute cache key: "/package-lock.json": not found
Now you know it’s a COPY input issue, not a runtime error.
Step 2: Confirm the file exists relative to the context root
If the build command is docker build ... ., then run:
ls -la package-lock.json
If it’s missing, fix your repo or Dockerfile. If it exists, continue.
Step 3: Check .dockerignore for exclusions
cat .dockerignore
If you see patterns that might match the file, test quickly by temporarily moving .dockerignore away:
mv .dockerignore /tmp/dockerignore.bak
docker build --progress=plain -t debugimg .
mv /tmp/dockerignore.bak .dockerignore
If the build succeeds without .dockerignore, your ignore patterns are the cause. Fix them properly (don’t leave .dockerignore removed—large contexts slow builds and may leak secrets).
Step 4: Verify you’re using the correct context and Dockerfile
Print your working directory and build command:
pwd
If Dockerfile is elsewhere:
docker build --progress=plain -f path/to/Dockerfile -t debugimg path/to/context
Remember: COPY sources are relative to the context, not the Dockerfile.
Step 5: Look for symlinks and permission issues
Broken symlinks:
find . -xtype l -print
Permissions:
# Replace with the path mentioned in the error
ls -la path
namei -l path
Fix or exclude problematic paths.
Step 6: Compare BuildKit vs legacy builder (diagnostic only)
DOCKER_BUILDKIT=0 docker build --progress=plain -t debugimg .
If legacy builder works but BuildKit fails, treat it as a sign your build inputs are ambiguous or your context has traversal issues. Fix the underlying file/path/ignore issue rather than disabling BuildKit permanently.
Best practices to prevent cache-key failures
Keep the build context small and intentional
A huge context increases the chance of permission/symlink/odd-file issues and slows builds.
Use .dockerignore to exclude:
.git
node_modules
dist
target
*.log
.env
*.pem
Then explicitly COPY only what you need.
Prefer multi-stage builds over host-generated artifacts
Instead of copying dist/ that may or may not exist:
- Build it in a builder stage
- Copy results into a clean runtime stage
This removes a whole class of “file not found” cache-key failures.
Avoid copying secrets; use BuildKit secrets instead
Don’t do:
COPY .env /app/.env
Do:
# syntax=docker/dockerfile:1.6
RUN --mount=type=secret,id=env,target=/run/secrets/env \
some-command-that-reads-it
Build:
docker build --secret id=env,src=.env -t myapp .
Be explicit with paths and naming
- Match case exactly (
config.jsonvsConfig.json) - Avoid fragile globs in
COPY - Use
WORKDIRand consistent directory structure
Pin the Dockerfile frontend if using advanced syntax
If you use --mount=type=cache|secret|ssh, add:
# syntax=docker/dockerfile:1.6
at the top of the Dockerfile. This ensures the builder interprets the syntax correctly.
Reference: useful commands
Build with maximum readable output
docker build --progress=plain -t myimg .
Show builders and BuildKit info
docker buildx ls
docker buildx inspect --bootstrap
Prune build cache
docker builder prune
docker builder prune --all --force
Find broken symlinks
find . -xtype l -print
Identify what’s huge in your context (helps decide .dockerignore)
du -sh ./*
Confirm which files are present
ls -la
ls -la path/to/thing
Summary of the most common fixes
If you want the highest-probability solutions in order:
- Run:
docker build --progress=plain ...and identify the exact failingCOPY/ADD/mount. - Verify the file exists in the build context path.
- Fix
.dockerignore(often the real culprit). - Fix the build context (build from repo root, not a subfolder).
- Fix case sensitivity and broken symlinks.
- Fix permissions or exclude unreadable paths.
- Prune/recreate builder if everything is correct but BuildKit state is corrupted.
If you paste the exact error line and the relevant Dockerfile snippet (especially the failing COPY/RUN --mount step) and your build command, I can map it to the specific root cause and give a targeted fix.