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:
docker compose versiondocker 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 |
| rebuild | package.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 /appCOPY package*.json ./RUN npm ci
RUN useradd --create-home --uid 1001 appCOPY --chown=app:app . /appUSER app
CMD ["npm", "run", "dev"]實際 image 使用的基底與啟動指令可以不同,但檢查方向相同:target 目錄存在、使用者有寫入權限、watch 所需的工具存在。看到同步失敗時,先進 container 檢查:
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:
docker compose up --watch如果不想把應用程式 log、build log 和同步事件混在一起,也可以使用專用指令:
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。
實務上可以把規則整理成這個判斷:
- 只改原始碼,且 dev server 能熱更新:sync。
- 改了需要安裝的依賴:rebuild。
- 改了需要重新讀取但不影響 image 的設定:sync+restart。
- 要部署到 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 沒有把變更排除。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。