Docker multi-stage build "COPY failed" troubleshooting
Fixes COPY failed errors in multi-stage Docker builds. Use when COPY cannot find the source, cross-stage COPY --from fails, or builds differ between local and CI. Covers build context paths, .dockerignore exclusions, and stage name mismatches. Not for ADD URL fetches, runtime permission issues, or cache misses.
TL;DR
COPY failed in a multi-stage Docker build almost always means the source path does not exist in the build context or in the named stage you are copying from. Check the exact failing COPY line, verify the source path relative to the right context (build context for plain COPY, the --from stage for cross-stage COPY), and watch for .dockerignore excluding the file.
Error / query
Docker multi-stage build "COPY failed" troubleshootingUse this skill when
docker buildfails withCOPY failed: no such file or directoryorfile not found in build context- A cross-stage
COPY --from=[stage]fails while the stage built fine - The build works locally but fails in CI (or vice versa)
- You renamed stages or reorganized the Dockerfile recently
Not for this skill when
- The COPY succeeds but the file has wrong permissions at runtime (permission fix, not COPY)
- The failure is
ADDwith a URL (network fetch, different handling) - The build fails at a
RUNstep after COPY (the copy worked; debug the RUN) - You are debugging layer cache misses (caching, not COPY failures)
Steps
Step 1: Read the exact failing COPY instruction
docker build --progress=plain -t debug . 2>&1 | grep -B 8 "COPY failed"Expected: the full COPY line and which stage it is in. Multi-stage Dockerfiles have several COPYs; you need the exact one, including any --from= flag.
Step 2: Verify the source exists in the right context
grep -n "^FROM\|^COPY" Dockerfile
ls [source-path-from-failing-copy]Expected: for a plain COPY, the source must exist relative to the build context root (where you run docker build), not relative to the Dockerfile. For COPY --from=[stage], the source must exist in that stage's filesystem at that path.
Step 3: Check .dockerignore is not excluding the source
cat .dockerignore
docker build --progress=plain . 2>&1 | grep -i "transferring context" Expected: the failing source path is not matched by .dockerignore patterns. A broad pattern like * with too-narrow exceptions silently removes files from the context; the COPY then fails with "not found".
Step 4: Confirm the --from stage name or index is correct
grep -n "AS [a-z-]*" DockerfileExpected: the --from= value matches a stage name defined earlier in the file (or a numeric index). A renamed stage (AS builder changed to AS build) breaks every later COPY --from=builder; the error names the missing stage.
Step 5: Test the COPY in isolation with a minimal build
docker build --progress=plain --target [stage-before-failing-copy] -t stage-test .
docker run --rm stage-test ls -la [expected-source-dir]Expected: building up to the earlier stage and listing its filesystem shows whether the file was ever created there. If the producing stage never made the file, the bug is upstream of the COPY.
Variant phrasings
"docker copy no such file or directory"
The source is missing from the context. Steps 2-3: check the path relative to context root and .dockerignore.
"COPY --from not working docker"
The stage reference is wrong or the file does not exist in that stage. Steps 4-5.
"docker build works locally but COPY fails in ci"
The CI checkout has a different directory layout, or CI sets a different build context (e.g. a subdirectory). Compare ls of the context root in both.
Why it happens
COPY has two different source namespaces and people mix them up: without --from, the source is the client-side build context (filtered by .dockerignore); with --from, it is the filesystem of a previous stage. Add in stage renames and context-root confusion, and the file the COPY asks for simply is not where Docker looks.
Edge cases and pitfalls
- BuildKit and classic builder resolve contexts slightly differently; test with the same builder CI uses.
COPYwith a trailing-slash vs without changes directory-vs-contents semantics; a missing trailing slash can make it look like files vanished.- Symlinks in the context: COPY follows them at build time; a symlink pointing outside the context breaks the copy.
- Windows line endings or BOM in .dockerignore can make patterns not match; keep the file clean.
--fromalso accepts an external image name; a typo there pulls (or fails to find) an image instead of using your stage.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstiYK2WBvLhdpdnfmRVcKqQ
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.