Docker COPY 找不到檔案怎麼修?先確認 build context 與 .dockerignore
Docker 出現 COPY failed: file not found in build context 時,錯誤通常不是單純的檔名拼錯,而是 Docker 根本沒有把那個檔案放進此次 build 可讀的範圍。最常見的兩個原因是:建置指令最後的 context 不是你以為的資料夾,或 .dockerignore 把檔案排除了。
先記住一個規則:COPY 的來源路徑是相對於 build context,不是相對於 Dockerfile。 -f 只選擇 Dockerfile 的位置,不會改變 context 根目錄。
-f 和 build context 管的是不同事情
看下面兩個指令:
docker build -f docker/Dockerfile .這裡的 . 才是 context,Dockerfile 位於 docker/Dockerfile。因此 COPY package.json ./ 會尋找 repository 根目錄的 package.json。
docker build -f apps/web/Dockerfile apps/web這裡的 context 是 apps/web。COPY package.json ./ 會尋找 apps/web/package.json,而不是 repository 根目錄的 package.json。如果 Dockerfile 需要根目錄 lockfile,這個 context 就不包含它。
可以把解析方式想成:
| 部分 | 負責什麼 |
|---|---|
-f apps/web/Dockerfile | 告訴 Docker 使用哪個 Dockerfile |
最後的 . 或 apps/web | 決定 builder 可以看見哪些檔案 |
COPY apps/web ./apps/web | 從 context 根目錄尋找來源,再放入 image |
所以把 Dockerfile 從根目錄移到 docker/,不會讓 COPY ../package.json 變合法;要改的是 context 或檔案布局。
第二個檢查點是 context 根目錄的 .dockerignore
Docker 會在送出 build context 前,依照 context 根目錄的 .dockerignore 排除檔案。檔案即使在本機存在,也可能因為規則被移除,最後讓 COPY 找不到。
例如目錄如下:
.├── .dockerignore├── package.json├── pnpm-lock.yaml├── apps/│ └── web/│ ├── Dockerfile│ └── src/使用 docker build -f apps/web/Dockerfile . 時,如果 .dockerignore 有這些規則:
apps/web**/src那麼 COPY apps/web ./apps/web 的來源就可能已經不在 context 裡。先從「實際指令最後的路徑」找到 .dockerignore,再檢查來源路徑和父資料夾是否被排除:
pwdsed -n '1,240p' .dockerignore如果有多個 Dockerfile,Docker 也支援放在 Dockerfile 同一目錄、以 Dockerfile 名稱命名的 ignore file,例如 build.Dockerfile.dockerignore。這類 Dockerfile-specific ignore file 會優先於 context 根目錄的 .dockerignore,不要只看其中一份。
COPY 錯誤的固定診斷順序
不要一看到錯誤就隨意加 ../ 或把整個 repository 當 context。依序做以下檢查比較快:
- 印出實際指令。 找到所有 flags 之後的最後一個路徑,確定 context 的絕對位置。
- 從 context 根目錄找來源。 Dockerfile 的
COPY apps/web/package.json ./,要對應到<context>/apps/web/package.json。 - 檢查
.dockerignore。 搜尋來源檔案和父資料夾是否被排除,也要留意**和最後一條匹配規則。 - 檢查大小寫。 Linux builder 會區分
Package.json和package.json;不要以 macOS 本機檔案系統的結果代替 CI。 - 對照 CI 指令。 本機可能用
.,部署平台卻使用apps/web,兩者的COPY路徑不能混用。 - 修好後重新看下一個
COPY。 第一個缺檔修好後,下一個被 context 或 ignore 排除的來源才會顯現。
若檔案是建置前才產生,請把產生步驟放在 docker build 之前,並確認產物沒有被 .dockerignore 排除。若檔案是 secret,則應改用 Docker 支援的 secret 或 runtime 注入方式,不要把金鑰複製進 image layer。
兩種常見目錄布局怎麼選
Repository root 作為 context
Monorepo 或需要共用根目錄 lockfile 時,可以使用:
docker build -f apps/web/Dockerfile .Dockerfile 內的來源就以 repository 根目錄為準:
FROM node:22-alpine AS buildWORKDIR /app
COPY package.json pnpm-lock.yaml ./COPY apps/web ./apps/webApplication directory 作為 context
應用程式完全自足時,可以縮小 context:
docker build -f Dockerfile apps/web此時 COPY src ./src 會從 apps/web/src 讀取。若需要 repository 根目錄的檔案,就必須改用 root context,或先把建置輸入整理到 application directory。
context 不必為了「看得到檔案」就無限制放大。選擇包含所有必要輸入、又不會把 secrets、依賴資料夾和無關檔案送進 builder 的最小範圍,並把這個選擇固定在 CI 指令中。
這些修法看似有效,其實會留下問題
- 不要用
COPY ../file嘗試跳出 context;Docker 不允許這樣讀取上層檔案。 - 不要只移動 Dockerfile,就以為 context 跟著移動。
- 不要為了讓一次 build 通過而整份刪掉
.dockerignore。 - 不要讓本機用 root context、CI 用子目錄 context,卻共用同一份 Dockerfile 路徑。
- 不要把整個 repository 複製進 image;只放入建置必需的來源與產物。
如果你是在 Astro、Vite 或其他前端專案中建置靜態站,先確認 lockfile、source 和 build 設定的相對位置,再處理最後交給 Caddy 或其他伺服器的 dist。若專案同時使用多份 Compose 設定,也可以搭配 Docker Compose include 的路徑與變數檢查 一起確認相對路徑。
結論:先修 context 邊界,再修 COPY 路徑
COPY failed 的排查順序可以縮成三件事:先看 build 指令最後的 context,再看 context 根目錄與 Dockerfile-specific 的 .dockerignore,最後才調整 COPY 路徑。只要 Dockerfile、context 和 ignore 規則對同一個目錄布局達成共識,這類錯誤通常不需要用 ../ 或複製整個 repository 來掩蓋。
常見問題
Q: COPY 來源路徑是相對於 Dockerfile 嗎?
A: 不是。COPY 來源是相對於 build context 根目錄;-f 只指定 Dockerfile 的位置,不會改變 context。
Q: 本機看得到檔案,為什麼 Docker 還是說找不到?
A: 先比較本機和 CI 的最後一個 context 路徑,再檢查 context 根目錄的 .dockerignore,以及同目錄的 Dockerfile-specific ignore file。檔案可能存在於磁碟,但已被排除在 builder 可讀的 context 之外。
Q: 可以用 COPY ../package.json 讀 repository 根目錄檔案嗎?
A: 不行。Docker 不允許 COPY 逃出 build context。要嘛把 context 改成包含該檔案的根目錄,要嘛調整建置輸入讓 application directory 自足。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。