1361 字
7 分鐘

Docker Compose Watch 怎麼用?用 sync、rebuild 分開本機開發流程

使用 Docker Compose 開發時,如果每次修改程式碼都要手動停止、build、再啟動 container,問題通常不在 Dockerfile,而是本機開發流程沒有把「可同步的原始碼」和「必須重建 image 的依賴」分開。

Docker Compose Watch 透過 develop.watch 定義檔案變更規則,讓 Compose 依變更內容選擇同步、重啟或重建。它適合本機 inner loop,不是 production 部署策略。

先確認 Compose Watch 的使用邊界#

官方文件目前列出的前置條件是 Docker Compose 2.22.0 以上。Watch 也依賴服務有 build 設定,因為它要從本機原始碼建立可更新的服務;只有 image 的預建映像檔服務不會照這個流程追蹤原始碼變更。

先確認專案實際使用的 Compose:

Terminal window
docker compose version
docker compose config --quiet

第一個指令確認版本,第二個指令先驗證 Compose 是否能解析目前設定。若設定檔有多份覆寫檔或環境變數,應在與日常開發相同的 -f 參數下執行。

用三種 action 分開三種變更#

最小的 Node.js 服務可以這樣設定:

services:
web:
build: .
command: npm run dev
ports:
- "3000:3000"
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
- node_modules/
- action: rebuild
path: package.json
- action: sync+restart
path: ./config
target: /app/config

三條規則的分工是:

action適合的變更會做什麼
sync支援 hot reload 的原始碼或靜態檔將變更檔案同步進既有 container
rebuildpackage.json、requirements.txt 或 Dockerfile 相關依賴重建 image 並重建服務 container
sync+restart不必重建 image,但程式需重新讀取的設定檔先同步檔案,再重啟服務

如果只是同步檔案卻不會觸發應用程式重新載入,改用 sync+restart;如果依賴變更會影響 image 內容,就不要把它當成 sync。

sync 的 path、target 與 ignore 要對得上#

path 是主機端、相對於 Compose project directory 的路徑;target 是 container 內要寫入的位置。以 path: ./src 和 target: /app/src 為例,主機的 src/routes/index.ts 會同步到 container 的 /app/src/routes/index.ts。

ignore 的相對位置是以目前 watch rule 的 path 為基準,不是整份 Compose 專案的根目錄。這也是 node_modules/ 常常寫錯位置的原因。它會被同步到 container 嗎?要看你把 path 設在哪一層。

JavaScript 專案不要把主機的 node_modules 直接同步進 Linux container。原生套件可能依作業系統與 CPU 架構不同而不相容;讓 image 在 container 內安裝依賴,再只同步原始碼,較容易維持開發環境一致。

權限與 image 內的工具不能漏#

Compose Watch 需要服務 image 內有 stat、mkdir 和 rmdir 等基本指令,並且 container 的 USER 必須能寫入 target。如果使用非 root 使用者,Dockerfile 要在複製檔案時指定正確 owner:

FROM node:22
WORKDIR /app
COPY package*.json ./
RUN npm ci
RUN useradd --create-home --uid 1001 app
COPY --chown=app:app . /app
USER app
CMD ["npm", "run", "dev"]

實際 image 使用的基底與啟動指令可以不同,但檢查方向相同:target 目錄存在、使用者有寫入權限、watch 所需的工具存在。看到同步失敗時,先進 container 檢查:

Terminal window
docker compose exec web sh -lc 'id && command -v stat && command -v mkdir && command -v rmdir && ls -ld /app/src'

不要為了讓 Watch 跑起來就把整個服務改回 root;先縮小到需要寫入的目錄,並修正 Dockerfile 的 owner。

啟動與觀察 Watch#

設定完成後,可以讓 Compose 一起啟動服務並進入 watch:

Terminal window
docker compose up --watch

如果不想把應用程式 log、build log 和同步事件混在一起,也可以使用專用指令:

Terminal window
docker compose watch

修改 src 內的檔案時,預期看到同步與 hot reload;修改 package.json 時,預期是 rebuild。若兩者都沒有發生,先確認變更檔案確實落在 path 內,且沒有被 ignore 或 .dockerignore 排除。

Watch 不取代正式環境設定#

Compose Watch 的目標是快速迭代,不是讓 production container 依賴主機檔案。正式環境仍應建立可重現的 image,並在部署時執行 build、migration、health check 和回滾流程。

如果同一個 Compose 專案還有資料庫、Redis 或 Mailpit,核心服務的啟動條件可以搭配 healthcheck 與 service_healthy 的啟動排查。要把 Mailpit、Adminer 之類輔助工具變成選用服務,則另外使用 Docker Compose profiles 分離本機工具,不要把所有服務都掛進 Watch。

實務上可以把規則整理成這個判斷:

  1. 只改原始碼,且 dev server 能熱更新:sync。
  2. 改了需要安裝的依賴:rebuild。
  3. 改了需要重新讀取但不影響 image 的設定:sync+restart。
  4. 要部署到 production:離開 Watch,走正式 image pipeline。

常見問題#

Q: Docker Compose Watch 和 bind mount 一樣嗎?#

A: 兩者都能讓 container 看到主機檔案,但用途不同。bind mount 是持續掛載目錄;Watch 是依規則選擇同步、重啟或重建,還能用 ignore 把 node_modules 等不該同步的內容排除。它是開發流程的補充,不是 bind mount 的全面替代品。

Q: 為什麼 package.json 要用 rebuild,不能用 sync?#

A: package.json 變更通常代表 image 內的依賴需要重新安裝。單純同步檔案不會更新已存在的 node_modules,因此應讓 Compose rebuild image,再重新建立服務。

Q: Watch 一直同步失敗,要先查什麼?#

A: 先查 Compose 版本、服務是否有 build、image 內是否有 stat/mkdir/rmdir,以及 container 使用者是否能寫入 target。接著確認 path、ignore 和 .dockerignore 沒有把變更排除。

參考資料:

Docker Docs:Use Compose Watch

Docker Docs:Compose Develop Specification

Docker Docs:docker compose watch

Docker Compose Watch 怎麼用?用 sync、rebuild 分開本機開發流程
https://laplusda.com/posts/docker-compose-watch-local-development/
作者
Zero
發佈於
2026-08-07
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。