Docker Compose 完整教學 2026:compose.yaml 到 AI models

Docker Compose(現行 v5.6.1、GitHub 38,491 星、Apache-2.0)是自架服務的事實標準:一個 compose.yaml 定義服務、網路、volume 與密鑰,一行指令起停整套stack。本文用官方文件逐項驗證 2026 年真正該會的東西——depends_on 三種 condition 與 healthcheck、profiles 分環境、全新的 pre_start init container、watch 熱更新、secrets 與 .env 的差異、用 models 宣告本地 LLM,以及 12 個實際踩雷與 Compose Bridge 轉 Kubernetes。

  • Dennis
  • 15 分鐘閱讀
Docker Compose 完整教學 2026:compose.yaml 到 AI models

Docker Compose 是用一個 YAML 檔描述「一整套容器服務」的工具:服務、網路、儲存卷、密鑰、甚至 AI 模型都寫在同一個 compose.yaml,再用 docker compose up -d 一次起停。它現在是 v5.6.1(GitHub 38,491 星、Apache-2.0),2026 年新增的 pre_start、watch、models 已經讓它從「開發用的小工具」變成能管生產環境的規格。

站上二十幾篇自架教學——Tailscale、Caddy、Traefik、Nextcloud、Paperless、Uptime Kuma、Jellyfin、Syncthing——每一個的安裝步驟都是「貼一段 compose.yaml」。但很少有人回頭講清楚:這段 YAML 為什麼這樣寫、depends_on 到底保證了什麼、.env 跟 secrets 差在哪、以及 2026 年的 Compose 已經多了哪些以前要另外裝工具才有的功能。

這篇就是補上那一段。所有語法都對照 Docker 官方文件(2026-10-26 查證),範例可直接複製。

一表看懂:Compose 的現況與定位

項目內容
專案docker/compose(Go 撰寫)
星數/授權38,491 星、5,887 forks、Apache-2.0(2026-10-26 以 GitHub API 查詢)
最新版本v5.6.1(2026-10-21 發布)、v5.6.0(10-02)、v5.5.1(09-03)
檔案名稱建議 compose.yaml(也接受 compose.yml、docker-compose.yaml)
需要的主題不需要;version: 頂層欄位已被規格標示為 obsolete,別再寫
適用場景單機多容器、開發環境、小型生產、CI
不適用場景多節點排程、跨機器自動擴縮(要 Swarm/Kubernetes,或改用 Compose Bridge 轉 manifest)

先講一個很多人卡住的觀念:Compose 不是容器,是「宣告」。 它把你要的狀態寫成文件,然後比對現況、算出差異、只動需要動的地方。所以 docker compose up -d 第二次執行幾乎不會有任何動作——這不是它偷懶,是它正確。

Compose 應用模型:五個頂層元素

一份 compose.yaml 的骨架只有這幾層:

頂層元素作用
services每個容器(或 provider 能力)的定義
networks自訂網路;沒寫也會自動建一個 <專案名>_default
volumes具名儲存卷,資料放在 Docker 管理區而非容器內
configs非敏感設定檔,直接掛進容器
secrets敏感資料,掛在 /run/secrets/<名稱>
modelsAI 模型宣告(2026 新制,稍後詳談)

同一網路內的服務,可以直接用服務名稱當主機名互連。這就是為什麼 Nextcloud 的設定裡資料庫主機可以只寫 db、Redis 只寫 redis——不用管 IP、不用管容器重啟後位址變了。

最小可用範例:WordPress + MySQL

以下這份可以直接存成 compose.yaml 執行,是理解全部概念的最小樣本:

services:
  db:
    image: mysql:8.4
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u root -p$$(cat /run/secrets/db_root_password) --silent"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    secrets:
      - db_root_password
      - db_password

  wordpress:
    image: wordpress:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
        restart: true
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_root_password:
    file: ./db_root_password.txt
  db_password:
    file: ./db_password.txt

volumes:
  db_data:

三個重點:

  1. $$ 是跳脫符號。Compose 會先用 $ 做變數插值,要傳「字面上的錢字號」給容器時必須寫 $$,上面 healthcheck 裡的 $$(cat ...) 就是這個原因。
  2. _FILE 結尾的環境變數是官方映像的慣例,不是 Docker 的功能。MySQL、Postgres 這類映像看到 MYSQL_ROOT_PASSWORD_FILE 會自己去讀檔,所以密碼不會出現在 docker inspect 的 env 裡。
  3. db_data 這種具名 volume 第一次建立時擁有者是 root;如果服務用非 root 使用者跑,就會撞權限——這個問題下面用 pre_start 解。

指令表:日常真正會用到的 12 個

指令用途備註
docker compose up -d背景啟動全套有異動的容器才會重建
docker compose up --wait啟動並等到 healthy隱含 detached,CI 首選
docker compose up --watch啟動+監看檔案變更需develop.watch 設定
docker compose ps看狀態與 health-a 含已停止
docker compose logs -f web追單一服務日誌--tail=100 限行數
docker compose exec web sh進入執行中的容器服務必須是 running
docker compose run --rm web sh跑一次性指令不會覆蓋既有容器
docker compose config印出合併後的最終設定排錯第一招,看插值結果
docker compose config --quiet只驗證語法適合放進 CI
docker compose pull拉新映像配合 up -d 才是完整更新
docker compose restart web重啟(不重建)改 image 無效
docker compose down停並移除容器/網路-v 會一併刪 volume

最後一項是本站最常被問到的資料遺失原因:docker compose down -v 的 -v 是「把具名與匿名 volume 一起刪掉」。資料庫、Nextcloud 檔案、Uptime Kuma 的 kuma.db 全都在 volume 裡,按下去就沒了。備份策略請看文末的延伸閱讀。

啟動順序:depends_on 到底保證什麼

這是誤解率最高的一條。官方文件寫得很清楚:啟動時 Compose 只等到容器「running」,不等「ready」。 所以資料庫容器已經 running、但還沒能接受連線時,應用服務照樣會被拉起來,然後連線失敗。

解法是把 condition 疊上去:

condition意思適用
service_started只要啟動就往下走(預設行為)對相依性不敏感的服務
service_healthy等 healthcheck 通過資料庫、訊息佇列
service_completed_successfully等它執行完成且 exit 0一次性遷移、seed 資料

加上 restart: true(如上面範例)還有一層效果:當 db 因為 docker compose restart 之類的操作被重啟時,web 會跟著被重啟,避免應用握著斷掉的連線不放。

healthcheck 的四個時間參數建議這樣理解:interval(多久檢查一次)、timeout(單次多久算失敗)、retries(連續失敗幾次判定 unhealthy)、start_period(啟動寬限期,這段時間內的失敗不計入 retries)。資料庫這種要跑初始化腳本的服務,start_period 給 30 秒以上是常態。

profiles:一份檔案管 dev 與 prod

不要為了「生產環境不想開 phpMyAdmin」而複製兩份 compose.yaml。用 profiles:

services:
  backend:
    image: myapp-backend
  db:
    image: postgres:18
  phpmyadmin:
    image: phpmyadmin
    depends_on: [db]
    profiles: [debug]
  mailhog:
    image: mailhog/mailhog
    profiles: [debug, mail]

行為規則:

  • 沒有 profiles 的服務永遠啟用(backend、db 屬於這類,建議核心服務都不要掛 profile)。
  • docker compose --profile debug up 才會拉起 phpmyadmin。
  • 可以用多個 --profile,或 COMPOSE_PROFILES=debug,mail 環境變數。
  • --profile "*" 一次開全部。
  • 在指令列直接點名某個有 profile 的服務(例如 docker compose run phpmyadmin)時,Compose 會自動啟用它的 profile,不用手動加參數。

pre_start:2026 最值得學的新功能

以前要「在服務啟動前跑資料庫遷移」,標準做法是把它寫成另一個服務、restart: "no",再讓主服務用 depends_on: condition: service_completed_successfully 等它。缺點是 docker compose ps 會多一個 exited 的服務、多步驟時 depends_on 會變成一張網。

現在改用 init container(Compose 稱之為 pre_start 生命週期掛鉤)。這個功能是 v5.3.0(2026-07-02) 才引入的原生支援,舊版會直接回 additional properties 'pre_start' not allowed——升版前先確認 docker compose version:

services:
  app:
    image: myapp:latest
    depends_on:
      db:
        condition: service_healthy
    pre_start:
      - command: ["./manage.py", "migrate"]
      - command: ["./manage.py", "loaddata", "fixtures.json"]

  db:
    image: postgres:18

它的運作規則(官方文件明列):

  • 每一步都在獨立的臨時容器裡跑,依序執行,前一步 exit 0 才跑下一步。
  • 預設繼承服務的 image,可用 image: 覆寫。
  • 加入與服務相同的網路,所以能連到 depends_on 的服務。
  • 共享服務的 volume 掛載,寫入的檔案立刻對服務可見。
  • 任一步非 0 退出 → 服務不啟動,且 Compose 會保留該 hook 容器讓你看日誌:docker ps -a --filter label=com.docker.compose.hook=pre_start,再 docker logs <id>。

第二個殺手級用法是修 volume 權限。具名 volume 預設 root 擁有,非 root 服務寫不進去:

services:
  app:
    image: myapp:latest
    user: "1000:1000"
    volumes:
      - data:/data
    pre_start:
      - image: busybox
        user: root
        command: sh -c 'chown -R 1000:1000 /data'

volumes:
  data:

限制也要知道:pre_start 每個服務只跑一次,不是每個 replica 一次(per_replica: true 尚未支援);擴容(scale up)不會重新觸發;只有在定義變更、上次失敗、或 --force-recreate 時才會重跑。至於靜態檔案與密鑰,官方明確建議直接用 configs/secrets,不需要動用 init container。

watch:把 bind mount 換成有規則的同步

開發時掛 bind mount 很常見,但整個目錄掛進容器有兩個副作用:node_modules 這種大量小檔會拖慢 I/O,而且主機與容器的作業系統/架構不同時,編譯產物不能共用。

Compose 的解法是 develop.watch:

services:
  web:
    build: .
    command: npm start
    develop:
      watch:
        - action: sync
          path: ./web
          target: /src/web
          initial_sync: true
          ignore:
            - node_modules/
        - action: sync+restart
          path: ./proxy/nginx.conf
          target: /etc/nginx/conf.d/default.conf
        - action: rebuild
          path: package.json

三種 action 的分工:

action行為典型用途
sync把變更檔案同步進容器,不重啟支援 hot reload 的框架(Vite、Flask)
sync+restart同步後重啟主程序改 nginx.conf、資料庫設定
rebuild重新 build image 並換掉容器改 package.json、requirements.txt

幾個實務細節:watch 只對用 build: 建置的服務有效(用 image: 的服務不會被追蹤);容器內需要有 stat、mkdir、rmdir 三個執行檔;容器使用者必須能寫入目標路徑(Dockerfile 用 COPY --chown 處理);glob 不支援,且 .dockerignore 的規則會生效。預設每次 rebuild 會清掉上一版 dangling image,要保留用 docker compose watch --prune=false。

.env 與 secrets:兩種完全不同的東西

這是最常被搞混的一組。用一張表分開:

.env(專案目錄)env_file:(服務層級)secrets:
給誰用Compose 自己做 ${VAR} 插值注入容器的環境變數掛成容器內的檔案
會不會出現在 docker inspect插值後會(成為容器 env)會不會
適用連接埠、映像 tag、路徑非敏感的應用設定密碼、API key、憑證
平台限制無無僅 Linux 容器(靠 bind mount 單檔實作)

secrets 的價值在於它不是環境變數。環境變數對所有行程可見、出錯時常被印進日誌,而 secret 是一個只有指定服務能讀的檔案,權限可控。官方範例的用法就是 file: 指向本機檔案,或 environment: NPM_TOKEN 從環境變數取値——後者常用在 build secret(build.secrets),避免把 npm token 寫進 image layer。

實務建議:.env 只放「就算外流也不太痛」的東西,真正的憑證走 secrets,並記得 .gitignore。順帶一提,Compose 有自己的信任模型(trust model)文件,講清楚它假設「Compose 檔案與執行它的使用者是同一個信任邊界」——所以不要把來路不明的 compose.yaml 直接 up。

把 AI 模型寫進 compose.yaml

2026 年 Compose 最明顯的變化,是它開始把 AI 模型當成一等公民。用頂層 models 宣告,服務再用 models: 綁定:

services:
  chat-app:
    image: my-chat-app
    models:
      llm:
        endpoint_var: AI_MODEL_URL
        model_var: AI_MODEL_NAME

models:
  llm:
    model: ai/smollm2
    context_size: 4096
    runtime_flags:
      - "--temp"
      - "0.1"

短語法(只寫 - llm)會自動產生 <名稱>_URL 與 <名稱>_MODEL 兩個環境變數,長語法可以自訂變數名稱。執行時由平台負責「拉模型、跑起來、把 endpoint 注入服務」——在 Docker Model Runner 啟用的環境下,這段就是本機 LLM 的標準啟動方式。

官方標示的前置條件是 Compose v2.38 以上(現行版本已到 v5.6.1)+ 支援 Compose 模型的平台。runtime_flags 直接透傳給推理引擎(llama.cpp/vLLM),所以溫度、top-k、--embeddings(embedding 模型必加)、--threads 都能在 YAML 裡宣告,不用再寫一層啟動腳本。

同一脈絡下還有 provider services:服務可以宣告 provider: {type: ...},代表這個「服務」不是容器,而是由第三方外掛提供的平台能力,Compose 負責呼叫它、拿到連線資訊、注入環境變數給依賴它的服務。例如 type: model 會去找 docker-model 外掛或 PATH 上的 model 執行檔。對自架族的意義是:基礎設施依賴開始可以宣告在同一份文件裡,不必再手寫 shell 去等外部資源。

多環境:override 檔與 include

三種合併方式,選一種就好:

方式寫法適用
自動 override放一個 compose.override.yaml,up 時自動疊上本機開發差異
手動 -fdocker compose -f compose.yaml -f compose.production.yaml up -d明確的環境分層
includeinclude: [{path: ../shared/monitoring.yaml}]跨團隊共用的子堆疊

生產環境的調整項目,官方建議是:移除應用程式的 bind mount(讓程式碼鎖在 image 裡,外部改不到)、換 host 連接埠、降低日誌詳細度、加上 restart 政策、補上日誌聚合器。這些寫進 compose.production.yaml 就好,不用複製整份檔案。

只更新單一服務時,標準兩步是:

docker compose build web
docker compose up --no-deps -d web

--no-deps 是關鍵,否則 Compose 會把你相依的資料庫等服務一起重建。遠端部署則只要設好 DOCKER_HOST、DOCKER_TLS_VERIFY、DOCKER_CERT_PATH,所有 docker compose 指令原封不動就能對遠端主機操作。

要上 Kubernetes?用 Compose Bridge

如果同一份 compose.yaml 遲早要搬到 K8s,官方有一條轉換路徑:Compose Bridge 會把 Compose 模型轉成 Kubernetes manifest 與 Kustomize overlay。

它的機制是把「轉換」包成一個 Docker image:輸入是解析後的 Compose 模型(/in/compose.yaml),輸出是任何格式(/out)。預設的 K8s 轉換用 Go template 寫,平台團隊可以自訂 template,把安全上下文、資源限制、labels、network policy 一次注入——開發者照樣寫 Compose,治理規則集中在一份 template 裡改一次、全部團隊下次轉換就生效。它也支援搭配 Docker Model Runner 的 LLM 應用。

12 個實際踩雷

踩雷症狀解法
down -v 當成 down資料庫、檔案全消失備份 policy 先做;只停服務用 stop
depends_on 沒加 condition應用先啟動、連線失敗用 service_healthy + healthcheck
healthcheck 裡的 $ 沒跳脫變數被 Compose 吃掉,指令壞掉寫 $$
非 root 服務寫不進 volumepermission deniedpre_start 跑 chown,或調整 image 使用者
改了 compose.yaml 卻沒生效舊設定繼續跑確認沒加 --no-recreate;必要時 --force-recreate
restart 當成「重新讀 image」更新 image 後版本沒變pull + up -d,或 build + up -d
.env 放了資料庫密碼密碼出現在 inspect/日誌改用 secrets
兩個專案同名容器/網路互撞用 -p 指定專案名,或 COMPOSE_PROJECT_NAME
只開 expose 沒開 ports主機連不到對外要 ports,expose 只給同網路
watch 在有 image: 的服務上沒反應檔案改了但沒同步watch 只支援 build: 的服務
專案名取自資料夾換目錄就變新專案、volume 重建明確設 name: 或 -p
把 Compose 當 K8s 用沒有跨節點、沒有自動修復單機夠用就好;要多節點走 Swarm/Bridge

該不該用 Compose?三個對照

需求ComposeKubernetes純 docker run
單機多容器✅ 最適過重要自己寫腳本
宣告式設定進版控✅ 一份 YAML✅❌
跨節點排程/自動擴縮❌✅❌
學習成本低高最低
從開發到生產同一份設定✅部分❌

一句話:一台機器上跑得動的服務,用它;要跨機器、要自動排程,再往上換。 中間的過渡期就用 Compose Bridge 轉 manifest,不必重寫。

站內延伸閱讀

資料來源與查證方法

  • 一手來源:Docker 官方文件 docs.docker.com/compose/(來源 repo docker/docs,Apache-2.0),逐篇對照 compose-application-model、startup-order、profiles、init-containers、file-watch、use-secrets、models-and-compose、provider-services、production、multiple-compose-files、bridge 與 docker compose up CLI 參考頁。
  • 版本與星數:api.github.com/repos/docker/compose(38,491 星、5,887 forks、Apache-2.0)與 releases API(v5.6.1,2026-10-21),查證時間 2026-10-26。
  • 官方自述 vs 本文補充:compose.yaml 為建議檔名、version: 已 obsolete、pre_start 的 per_replica 尚未支援、secrets 僅支援 Linux 容器、watch 只追 build: 服務,以上皆為官方文件明載。表格中「適用場景」的判斷、踩雷清單與最後的三方對照表,是本文依實務經驗整理的建議,不是官方立場。
  • 原始來源連結:
    • Compose 官方文件總覽:https://docs.docker.com/compose/
    • 應用模型(services/networks/volumes):https://docs.docker.com/compose/intro/compose-application-model/
    • 定義 AI 模型(models):https://docs.docker.com/ai/compose/models-and-compose/
    • init container(pre_start):https://docs.docker.com/compose/how-tos/init-containers/
    • 啟動順序與 healthcheck:https://docs.docker.com/compose/how-tos/startup-order/
    • Compose Watch:https://docs.docker.com/compose/how-tos/file-watch/
    • profiles:https://docs.docker.com/compose/how-tos/profiles/
    • secrets:https://docs.docker.com/compose/how-tos/use-secrets/
    • provider services:https://docs.docker.com/compose/how-tos/provider-services/
    • 多檔案與 include:https://docs.docker.com/compose/how-tos/multiple-compose-files/
    • 生產環境設定:https://docs.docker.com/compose/how-tos/production/
    • Compose Bridge:https://docs.docker.com/compose/bridge/
    • docker compose up CLI 參考:https://docs.docker.com/reference/cli/docker/compose/up/
    • 原始碼與版本:https://github.com/docker/compose/releases
  • 未實測部分:本文未在真實生產環境跑過 models 與 provider services(需要 Docker Model Runner 與外掛平台),語法與前置條件取自官方文件。YAML 片段已用本機 docker compose config(v5.1.0)做語法驗證:WordPress 範例、watch、models 三塊通過;pre_start 兩塊在本機版本回 additional properties 'pre_start' not allowed,原因是該功能自 v5.3.0(2026-07-02) 才引入、本機版本早於它,語法本身依官方文件撰寫,但未在支援的版本上實跑。實際行為仍以你環境的 Compose 版本為準。

📬 訂閱 most.tw 電子報

每週精選 AI 工具教學與技術乾貨,直接送到你的信箱。免費、隨時可退訂。

訂閱即表示同意收到 most.tw 電子報,隨時可一鍵退訂。

💬 有問題想討論?加 LINE 聯絡我

歡迎透過 LINE 官方帳號直接留言,我會盡快回覆你的問題。

加入 LINE 好友