2026.11 / Docker contexts 083
Coolify Docker COPY failed: fix “file not found” in the build context
A Coolify deployment can fail at a Dockerfile line such as COPY package.json ./, COPY dist/ /usr/share/nginx/html/, or COPY apps/web . even though the file exists on your laptop. BuildKit may report “failed to calculate checksum”, “not found”, or “failed to compute cache key”.
Those messages usually mean that the source does not exist inside the build context presented to Docker. The file may be uncommitted, outside Coolify’s base directory, removed by .dockerignore, named with different letter case, or expected to be generated in a stage where it was never created.
COPY cannot see your workstation or arbitrary paths around the Dockerfile. It can see only the selected context, prior build stages, named contexts, and images.
Read the failing source as Docker reads it
In a normal instruction, the source path is relative to the root of the build context—not relative to the Dockerfile’s own directory. Coolify’s Dockerfile build pack combines its Base Directory and Dockerfile Location settings to select these boundaries. A valid Dockerfile path does not prove that its sibling files are in the context.
| Evidence | Likely cause | Repair boundary |
|---|---|---|
File exists locally but not in git show | Untracked, ignored, or not pushed | Git commit |
| File is in Git but above the Coolify base directory | Context is too narrow | Coolify base directory or repository layout |
| Build log transfers an unexpectedly tiny context | .dockerignore excluded required input | Ignore rules |
| Works on a case-insensitive laptop only | Filename case differs | Committed path and Dockerfile spelling |
COPY --from=build fails | Wrong stage, source path, or missing build output | Multi-stage build |
| Correct commit still builds the wrong directory | Coolify source settings do not match the repository | Application configuration |
1. Capture the exact failed deployment
Start with the deployment record, not a broad cache purge. Record the checked-out commit, branch, repository, build pack, base directory, Dockerfile location, failing Dockerfile line, and full error. Keep the deployment identifier as returned; it need not look like a conventional UUID.
The source operand matters. These instructions have different namespaces:
COPY package.json ./
COPY --from=build /app/dist/ /usr/share/nginx/html/
COPY --from=nginx:alpine /etc/nginx/nginx.conf /tmp/default.conf
The first reads from the build context, the second from a named earlier stage, and the third from an image. Do not troubleshoot all three as if they were missing Git files.
2. Prove the file is in the deployed Git commit
A local editor tab is not deployment evidence. Ask Git whether the exact commit contains the exact path and case:
git show --stat --oneline EXPECTED_COMMIT
git cat-file -e EXPECTED_COMMIT:package.json
git cat-file -e EXPECTED_COMMIT:apps/web/package.json
git ls-tree -r --name-only EXPECTED_COMMIT
Use the path relevant to your project. If git cat-file fails, check git status --short, .gitignore, the pushed branch, submodules, and large-file handling. Commit a required source file; do not generate an empty placeholder in production merely to make COPY pass.
Also verify filename case. Linux treats Package.json, package.json, and package.JSON as different names. If a case-only rename is not being recorded on a case-insensitive workstation, perform an explicit two-step git mv and inspect the staged diff.
3. Draw the context boundary
Suppose a monorepo looks like this:
repository/
├── package-lock.json
├── packages/shared/
└── apps/web/
├── Dockerfile
├── package.json
└── src/
If Coolify’s base directory is /apps/web, Docker can see package.json and src, but it cannot copy the repository-root lockfile or packages/shared. Moving the Dockerfile does not widen that context. Likewise, COPY ../package-lock.json ./ cannot be used to escape it.
There are two sound designs:
- Set the build context to the repository root and set the Dockerfile location to the web app’s Dockerfile; or
- Make the application directory self-contained, including every manifest and source tree its build requires.
Choose based on the project’s real dependency boundary. Widening the context to the repository root is not automatically wrong, but pair it with a reviewed .dockerignore so unrelated artifacts and local credentials are not transmitted to the builder.
4. Reproduce Coolify’s context locally
Run the same Dockerfile and context combination. The final argument is the context; -f selects the Dockerfile:
# Repository-root context, nested Dockerfile
docker build --progress=plain \
-f apps/web/Dockerfile \
-t web-context-check .
# Narrow app-only context
docker build --progress=plain \
-f apps/web/Dockerfile \
-t web-context-check apps/web
These two commands can use the same Dockerfile while exposing different files to COPY. Reproduce from a clean clone or detached worktree of the expected commit so untracked local files cannot create a false pass.
A no-cache build can help reveal stage execution, but it cannot make a missing source enter the context. Fix the source boundary first.
5. Audit .dockerignore in the context root
Docker removes matching files before sending the context. A required file excluded by .dockerignore is therefore indistinguishable from an absent file to COPY. Check the ignore file at the context root and any Dockerfile-specific ignore file.
# Risky when the image needs prebuilt output
dist
# Risky when a later COPY expects a nested lockfile
**/package-lock.json
# Broad exclusion with a deliberate exception
*
!package.json
!package-lock.json
!src/**
!public/**
Exception order matters: the last matching rule determines whether a path is included. If a directory itself is excluded, adding only a child exception may not make the directory traversable as intended. Prefer a short allowlist only when you can test every required input.
Do keep genuinely unnecessary or sensitive local material out of the context: version-control metadata where unneeded, dependency directories, test output, logs, local environment files, editor state, and deployment credentials. Never “fix” a build by removing all ignore rules and sending the whole workstation directory.
6. Separate source files from generated artifacts
A common failing pattern expects a host-built dist directory:
FROM nginx:alpine
COPY dist/ /usr/share/nginx/html/
This works locally only after a developer has run the frontend build. A clean Coolify checkout has no generated directory unless it is committed or created during the image build. For most applications, generate it in a builder stage and copy from that stage:
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist/ /usr/share/nginx/html/
Now the final COPY reads from the filesystem produced by the build stage. If it fails, inspect the build command and output directory. Frameworks may write build, out, .output/public, or another configured path rather than dist.
7. Check multi-stage names and paths
For COPY --from=build, verify that:
- a preceding
FROM … AS builduses that exact stage name; - the build command actually completed;
- the source path is correct inside the stage;
- the path is not confused with a host or repository path; and
- the selected target did not skip the stage that produces the artifact.
Add a temporary non-secret listing immediately after the build command if the output path is uncertain. List only application directories, not the entire filesystem or environment. Remove the diagnostic once the Dockerfile contract is clear.
8. Handle monorepo manifests deliberately
Package-manager workspaces often need root manifests plus selected workspace manifests before dependency installation. A Dockerfile under apps/web can still use a repository-root context:
FROM node:22-alpine AS build
WORKDIR /workspace
COPY package.json package-lock.json ./
COPY apps/web/package.json apps/web/package.json
COPY packages/shared/package.json packages/shared/package.json
RUN npm ci
COPY apps/web apps/web
COPY packages/shared packages/shared
RUN npm --workspace apps/web run build
The exact files depend on the workspace manager, but the principle is stable: context, manifests, install root, and source layout must describe the same graph. If dependency installation then fails because the committed lockfile is inconsistent, follow the separate Coolify npm ci lockfile recovery guide rather than weakening the install.
9. Commit, deploy once, and prove the live release
After changing the repository layout, Dockerfile, ignore rules, or Coolify settings, run the production Docker build locally from a clean checkout. Inspect the diff for accidental environment files or credentials, then push the complete change.
git diff --check
git status --short
git diff -- Dockerfile .dockerignore
docker build --progress=plain -t app-context-check .
Trigger one Coolify deployment and poll it to a terminal status. In the deployment log, confirm the expected commit, base directory, Dockerfile, successful COPY step, completed build, and healthy replacement container. Finally, request a real route and assert unique content from the new release. An HTTP 200 alone can be a stale page, proxy fallback, or single-page-app fallback.
Fixes that usually make the problem worse
- Using
COPY ../file: a Dockerfile cannot reach above its context. - Purging every build cache: cache does not create a source missing from the context.
- Copying the whole repository without review: this can increase build time and expose local-only files to the builder.
- Committing generated output reflexively: a builder stage is usually a cleaner source of production artifacts.
- Deleting
.dockerignore: fix the one wrong pattern while preserving the security and performance boundary. - Changing path case until a laptop passes: prove the exact committed spelling on a case-sensitive checkout.
- Editing the live container: the next deployment will rebuild from source and discard the change.
Compact recovery order
- Capture the failed commit, branch, build pack, base directory, Dockerfile location, instruction, and complete error.
- Identify whether the source belongs to the context, an earlier stage, or an image.
- Prove the exact path and case exist in the deployed commit.
- Map Coolify’s base directory to Docker’s context root.
- Reproduce the exact Dockerfile/context pair from a clean checkout.
- Audit root and Dockerfile-specific ignore rules.
- Generate build artifacts in a builder stage where appropriate.
- Test the image, review the diff, deploy once, and verify unique live content.
Authoritative references: Docker’s official build-context guide defines which files COPY can access and how .dockerignore removes them; the Dockerfile COPY reference documents context and --from sources; Docker’s CopyIgnoredFile check covers excluded inputs. Coolify’s official Dockerfile build-pack guide explains how Base Directory and Dockerfile Location establish the build.