Overview
The Docker/BuildKit error "failed to compute cache key: not found" usually means a COPY/ADD source path is missing from the build context. Common reasons include a too-aggressive .dockerignore, wrong build context directory, typos or case mismatches, or multi-stage COPY --from references that don’t exist.
This guide shows fast checks, a minimal repro, fixes, pitfalls, and performance tips.
Quickstart
- Verify the build context
- Run the build from the project root that contains the files you COPY.
- Ensure you didn’t use stdin builds that strip context (e.g., docker build - < Dockerfile).
- Check .dockerignore
- Remove patterns that exclude required sources, or re-include with !pattern.
- Confirm paths and case
- Paths in COPY are relative to the build context. Case is significant inside images, even on Windows hosts.
- Validate multi-stage names
- Ensure COPY --from uses an existing stage name or index, and the path exists in that stage.
- Get verbose output
- Use: docker build --progress=plain --no-cache .
Minimal working example (repro and fix)
The following intentionally fails, then shows the fix.
Failing setup
# Create a demo project
mkdir demo && cd demo
# Dockerfile that copies package manifests, then the app
cat > Dockerfile <<'EOF'
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]
EOF
# .dockerignore that wrongly excludes required files
cat > .dockerignore <<'EOF'
node_modules
.git
# Oops: these exclusions cause the error below
package.json
package-lock.json
EOF
# Minimal app files
cat > package.json <<'EOF'
{
"name": "cachekey-demo",
"version": "1.0.0",
"main": "server.js",
"scripts": { "start": "node server.js" }
}
EOF
echo 'console.log("ok")' > server.js
# Build (will fail):
DOCKER_BUILDKIT=1 docker build -t cachekey-demo --progress=plain .
You’ll see an error for the COPY step similar to:
- failed to compute cache key: "package.json": not found
Fix
# Keep useful ignores but allow required files into the context
cat > .dockerignore <<'EOF'
node_modules
.git
EOF
# Rebuild (should succeed)
docker build -t cachekey-demo --progress=plain .
Step-by-step diagnosis
- Confirm the context directory
- The last argument to docker build is the context. Example: docker build -f Dockerfile .
- If you pass -, you’re likely sending only the Dockerfile via stdin, yielding an empty context. Use a real directory as context.
- Inspect .dockerignore
- Look for patterns that exclude needed files (e.g., src/, package*.json, dist/).
- Use ! to re-include files if you exclude a directory:
- Example: exclude dist but keep dist/index.html by adding !dist/index.html.
- Verify file existence and case
- Ensure the source exists relative to the context, not relative to WORKDIR.
- Confirm case-sensitive matches: README.md is not README.MD inside the image.
- Check COPY source forms
- COPY fileA fileB ./ fails if either source doesn’t exist.
- Wildcards must match at least one file; otherwise, COPY fails.
- Multi-stage builds
- Ensure the stage name matches exactly:
- FROM node:20-alpine AS build
- COPY --from=build /app/dist/ /usr/share/nginx/html/
- Verify the path exists in the source stage (e.g., /app/dist/ was created).
- Symlinks and outside-context paths
- Docker won’t copy files outside the build context. Symlinks pointing outside are excluded.
- Git build contexts
- If building from a remote Git URL, ensure submodules and the needed files are present. Vendor or include them explicitly.
- Re-run with plain progress
- docker build --progress=plain --no-cache . provides clearer step logs.
Common causes and fixes
| Cause | Symptom | Fix |
|---|---|---|
| .dockerignore excludes source | COPY fails with not found | Remove or adjust pattern; use ! to re-include required files |
| Wrong build context | Files present locally but missing in build | Run docker build from project root; pass the correct context path |
| Typo or case mismatch | Path looks correct on Windows but fails in image | Match exact case; verify relative paths to context |
| COPY multiple sources, one missing | Generic not found error | Ensure every source exists; avoid over-broad wildcards |
| Multi-stage alias mismatch | COPY --from=builder but stage is build | Rename to match or update --from |
| Artifact missing in stage | COPY --from path doesn’t exist | Confirm build step creates the artifact and path |
| Symlink points outside context | Not found or silently excluded | Keep files inside context; resolve symlinks beforehand |
| Stdin build (no context) | Any COPY of local files fails | Provide a directory context; avoid docker build - < Dockerfile |
Pitfalls
- Using COPY package*.json ./ when only package.json exists is fine if it matches at least one file; if it matches none, the build fails.
- Excluding entire directories (e.g., src/) in .dockerignore while trying to COPY them later.
- Building from a subfolder that omits needed top-level files.
- Forgetting to rename COPY --from after refactoring stage names.
- Relying on host case-insensitivity; container filesystems are case-sensitive.
Performance notes
- Keep context small but correct: Use .dockerignore to exclude large, unneeded paths (node_modules, .git, logs). Re-include only the files you need.
- Order Dockerfile steps to maximize cache:
- COPY only dependency manifests first (e.g., package.json) and run installs before copying the rest of the source.
- Use BuildKit cache mounts for package managers to speed repeated builds:
- RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev
- Avoid invalidating cache by changing many files before early layers. Keep frequent edits in later COPY steps.
- For multi-stage builds, copy only final artifacts (e.g., dist/) into the runtime image.
Tiny FAQ
Q: Why does this mention cache if the file is missing? A: BuildKit computes cache keys per step using inputs. If a source is missing or excluded, the solver can’t resolve the key and fails with not found.
Q: How do I see what files are in the context? A: There’s no direct listing, but you can tar and inspect: tar -czf - . --exclude-from=.dockerignore | tar -tz to approximate what’s sent.
Q: Does ADD behave differently from COPY? A: Both fail if sources are missing. ADD can fetch URLs and auto-extract archives, but won’t bypass .dockerignore or outside-context rules.
Q: Why does it work with DOCKER_BUILDKIT=0? A: Classic builder sometimes emits different messages, but the underlying issue (missing or excluded files) remains. Fix the context or paths.