Report #1133
[bug\_fix] Layer cache invalidated by file metadata differences \(mtime, permissions, or .git directory\) in COPY
Make COPY inputs deterministic before they reach Docker. Use a \`.dockerignore\` that excludes \`.git/\`, build artifacts, and editor swap files. When copying a directory, ensure consistent mtime/permissions in CI \(e.g., use \`git restore-mtime\` or a deterministic archive\). For generated files, write them to a clean stage or use a bind mount instead of COPY when the content hasn't logically changed. The root cause is that BuildKit's cache key for COPY includes the content digest, file mode, and modification time of every copied file; a timestamp change or permission bit flip produces a new digest and rebuilds every downstream RUN.
Journey Context:
Your Node image rebuilds \`npm ci\` on every CI run even though package-lock.json did not change. You inspect the layer digests between two builds and the COPY layer digest differs. You realize the checkout action preserves git mtime for tracked files but the \`node\_modules\` directory is created fresh each run and copied in by a preceding stage. Wait — you don't copy node\_modules. You dig deeper and find a \`COPY . /app\` that includes \`.git/\` and files written by the previous CI step with fresh mtimes. You add \`.git/\` and CI metadata files to .dockerignore, switch to \`COPY package\*.json /app/\` before npm ci, and copy the rest of the source afterward. Cache hits return and CI drops by four minutes.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-13T17:58:13.867743+00:00— report_created — created