What this error means
When building an image, Docker sends a “build context” (files under the chosen directory, minus anything excluded by .dockerignore). COPY can only read from that context. If the source path doesn’t exist in the context, you’ll see:
COPY failed: file not found in build context or excluded by .dockerignore
Common causes:
- Running docker build from the wrong directory (wrong context)
- Path mismatch in COPY (relative paths, case sensitivity)
- .dockerignore excludes the file or directory
- Misusing -f Dockerfile path vs. build context path
- Multi-stage COPY --from using a path that doesn’t exist in that stage
Quickstart checklist (fast fixes)
- Run the build from the directory that contains the files you COPY.
- Example: docker build -t myimg .
- If your Dockerfile is elsewhere, keep the context correct:
- docker build -f path/to/Dockerfile path/to/context
- Verify the source path in COPY matches your repo layout (relative to context).
- Inspect .dockerignore for patterns excluding your files; remove or negate them.
- On case-sensitive systems (Linux), ensure exact casing in file and COPY path.
- For multi-stage, ensure the source exists in that stage: COPY --from=builder /out/app ./app
Minimal working example (MWE)
This example succeeds and demonstrates correct context, paths, and .dockerignore.
Files:
- Dockerfile
- src/hello.txt
- .dockerignore (empty or not excluding src)
Dockerfile:
FROM alpine:3.20
WORKDIR /app
# Copy from a directory that exists in the build context
COPY src/ ./src/
# Prove the file was copied
RUN test -f ./src/hello.txt
CMD ["sh", "-c", "cat ./src/hello.txt"]
.dockerignore (optional, empty for MWE):
# keep empty or avoid excluding src/
Build and run:
printf "hello\n" > src/hello.txt
docker build -t mwe .
docker run --rm mwe
Expected output:
hello
Reproduce the error and fix it
Error reproduction: exclude src/ in .dockerignore.
.dockerignore (bad):
src/
Build:
docker build -t broken .
Expected error:
COPY failed: file not found in build context or excluded by .dockerignore
Fix options:
- Remove the exclusion, or
- Narrow it, or
- Negate it to re-include specific files
.dockerignore (fixed, re-include hello.txt):
src/
!src/hello.txt
Alternatively, change Dockerfile paths to copy from a non-excluded directory.
Step-by-step diagnosis
- Confirm your build context
- Run docker build in the directory that contains your sources.
- If using -f, set the second argument to the intended context:
- docker build -f docker/prod/Dockerfile .
- docker build -f Dockerfile ./subdir
- Verify COPY source paths
- Paths are relative to the context, not the Dockerfile (unless you set -f inside the context). Avoid leading slashes.
- Example: if file is at app/src/index.js and context is repo root, use COPY app/src/index.js /app/
- Inspect .dockerignore
- Look for broad patterns like node_modules, dist, build, .* that may exclude needed files.
- Use negation (!) to re-include specific paths; order matters (negations must come after the exclusion).
- Check case and platform details
- Linux is case-sensitive; Windows/macOS filesystems may be case-insensitive. Keep consistent casing in repo and COPY.
- Multi-stage build verification
- For COPY --from=builder, ensure the path exists inside that stage’s filesystem, not the local context.
- Add a debug line: RUN ls -la /expected/path in the source stage.
- Ensure files exist before COPY
- Generated files must be present in context or created in a prior stage. You cannot COPY artifacts built on the host during the build unless they’re in the context and not ignored.
Common pitfalls
- Using absolute paths in COPY (e.g., COPY /file /dest) — source must be relative to context.
- Dockerfile outside context without correct -f and context arguments.
- .dockerignore too broad (e.g., src/, **/*.json) removing required files.
- Relying on host-only artifacts (e.g., files generated by a pre-build script) that aren’t committed or placed inside the context.
- Incorrect negation order in .dockerignore (negations must come after the rule they negate).
- Copying from a previous stage but the stage name is wrong or path missing.
Cause-to-fix cheat sheet
| Cause | How to detect | Fix |
|---|---|---|
| Wrong build context | docker build shows small context or missing files | Run from repo root or pass correct context path |
| Path mismatch | ls shows file path differs from COPY source | Adjust COPY to correct relative path |
| Excluded by .dockerignore | Pattern matches the missing path | Remove pattern, narrow it, or use ! negation |
| Dockerfile path confusion | Using -f without correct context | docker build -f path/to/Dockerfile path/to/context |
| Multi-stage path missing | RUN ls in source stage shows path absent | Create the file in the stage or correct COPY --from path |
Performance notes
- Use .dockerignore to shrink context (e.g., .git, node_modules, dist, coverage). Smaller contexts upload faster and cache more effectively.
- Keep required sources unignored; when in doubt, re-include small subsets with negation (e.g., !src/config/*.json).
- Optimize caching by copying dependency manifests first, then running installs, then COPY the rest. Example for Node.js:
COPY package*.json ./
RUN npm ci --only=production
COPY . .
- Prefer multi-stage builds to avoid copying large toolchains/artifacts into the final image.
- Use docker build --progress=plain to see context size and layer cache info.
Tiny FAQ
- Can I COPY files outside the build context?
- No. Docker only sees files under the context directory.
- How do I build when my Dockerfile is in a subfolder?
- docker build -f subdir/Dockerfile . (context is current dir), or docker build -f subdir/Dockerfile subdir (context is subdir).
- Do .dockerignore patterns support negation?
- Yes. Use !pattern to re-include. Order matters; negations must come after the exclude.
- Why does it work on my machine but fail in CI?
- CI may run from a different context or use a different .dockerignore set. Explicitly set -f and context, and audit .dockerignore.
- COPY from a stage still fails — is that the same error?
- Similar symptom, different cause: the file must exist in the source stage’s filesystem. Add RUN ls in that stage to verify.