目錄

compose.yaml 語法完整說明

compose.yaml 最上層主要有四個區塊:

name: myapp          # 專案名稱(選用,預設是資料夾名稱)

services:            # 要執行的容器
  ...

volumes:             # 具名 Volume
  ...

networks:            # 自訂網路(選用,預設會自動建立一個)
  ...

下面依照用途說明 services 底下最常用的設定,最後用一個完整的例子實際執行。

Compose 專案中的服務、專案網路、具名 Volume 與對應到主機的連接埠
一個 Compose 專案:服務名稱就是主機名稱,只有設定了 ports 的服務能從主機連線

image 與 build

使用現成的映像檔:

services:
  redis:
    image: redis:8-alpine

從 Dockerfile 建置:

services:
  app:
    build: .                     # 用目前資料夾的 Dockerfile

需要更多選項時:

services:
  app:
    build:
      context: ./backend
      dockerfile: Dockerfile.prod
      target: prod               # 多階段建置的目標階段
      args:
        APP_VERSION: "1.4.2"
    image: myorg/app:1.4.2       # 建置後的映像檔名稱

同時寫 build 和 image,Compose 會把建置結果命名為 image 的值,之後可以 docker compose push。

ports:連接埠

    ports:
      - "8080:80"              # 主機:容器
      - "127.0.0.1:5432:5432"  # 只開放本機
      - "9000-9002:9000-9002"  # 範圍

請加上引號。YAML 會把某些 數字:數字 的格式解讀成 60 進位的數字,造成奇怪的錯誤。

expose 只是說明用途,不會對外開放;同一網路中的服務本來就能互連。

volumes:掛載

services:
  db:
    image: postgres:18
    volumes:
      - pgdata:/var/lib/postgresql                               # 具名 Volume
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro       # Bind mount(相對路徑)

volumes:
  pgdata:
  • 以 ./ 或 ../ 開頭的是相對於 compose.yaml 的 Bind mount。

  • 只寫名稱的是具名 Volume,必須在最上層的 volumes: 宣告,實際名稱會加上專案名稱前綴(例如 shop_pgdata)。

  • 想使用在 Compose 之外建立的 Volume:

    volumes:
      pgdata:
        external: true

environment 與 env_file

    environment:
      NODE_ENV: production
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/app
    env_file:
      - .env.app

${...} 的值來自 .env 檔或目前的環境變數,詳見 環境變數與 .env 檔。

depends_on 與 healthcheck:等資料庫真的準備好

depends_on 只控制啟動順序:保證 db 容器先啟動,但不保證資料庫已經可以連線。PostgreSQL 啟動需要幾秒,應用程式可能因為連不上而失敗。搭配 healthcheck 和 condition: service_healthy,Compose 會等健康檢查通過才啟動應用程式。

實際範例(compose.yaml):

name: shop

services:
  app:
    image: postgres:18
    command: ["psql", "-h", "db", "-U", "shop", "-c", "SELECT 'app connected' AS status;"]
    environment:
      PGPASSWORD: ${DB_PASSWORD}
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:18
    environment:
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
      POSTGRES_DB: shop
    volumes:
      - pgdata:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U shop -d shop"]
      interval: 2s
      timeout: 3s
      retries: 20

  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: [tools]

volumes:
  pgdata:

app 用 PostgreSQL 的用戶端模擬一個「啟動時要連資料庫」的應用程式。.env 中設定 DB_PASSWORD=change-me。

先單獨啟動 db,觀察健康狀態的變化:

$ docker compose up -d db
[+] up 3/3
 ✔ Network shop_default Created                                                                 0.0s
 ✔ Volume shop_pgdata   Created                                                                 0.0s
 ✔ Container shop-db-1  Started                                                                 0.3s
$ docker compose ps
NAME        IMAGE         COMMAND                  SERVICE   CREATED        STATUS                                     PORTS
shop-db-1   postgres:18   "docker-entrypoint.s…"   db        1 second ago   Up Less than a second (health: starting)   127.0.0.1:5432->5432/tcp

幾秒後:

$ docker compose ps
NAME        IMAGE         COMMAND                  SERVICE   CREATED          STATUS                   PORTS
shop-db-1   postgres:18   "docker-entrypoint.s…"   db        10 seconds ago   Up 9 seconds (healthy)   127.0.0.1:5432->5432/tcp

狀態從 health: starting 變成 healthy。關掉後從頭啟動 app:

$ docker compose up app
[+] up 3/3
 ✔ Network shop_default Created                                                                 0.0s
 ✔ Container shop-db-1  Created                                                                 0.0s
 ✔ Container shop-app-1 Created                                                                 0.1s
Attaching to app-1
Container shop-db-1 Waiting
Container shop-db-1 Healthy
app-1  |     status
app-1  | ---------------
app-1  |  app connected
app-1  | (1 row)
app-1  |
app-1 exited with code 0

w Enable Watch   d Detach

Compose 先啟動 db,顯示 Waiting,等到 Healthy 才啟動 app,app 第一次連線就成功(app connected)。如果沒有 condition: service_healthy,app 會在資料庫還沒準備好時就去連線。

(最後一行 w Enable Watch d Detach 是 Compose 在前景執行時顯示的快捷鍵提示。)

健康檢查中的 pg_isready 加了 -h 127.0.0.1。不加的話,資料庫在第一次初始化期間就會被判定為 healthy,應用程式反而會連線失敗,實測見 PostgreSQL。MySQL 也有同樣的問題,見 MySQL。

condition 可用的值:

值意思
service_started容器已啟動(預設)
service_healthy健康檢查通過
service_completed_successfully該服務執行完畢且結束代碼為 0,適合資料庫遷移等一次性任務

另外 depends_on 底下加上 restart: true,db 重啟時 app 也會跟著重啟。

restart

    restart: unless-stopped

可用值:no、always、on-failure、unless-stopped(行為見 容器生命週期)。

command 與 entrypoint

覆蓋映像檔的 CMD / ENTRYPOINT:

    command: ["npm", "run", "dev"]
    entrypoint: ["/app/docker-entrypoint.sh"]

networks

預設情況下,所有服務都在同一個 專案名稱_default 網路中。需要隔離時,可以自訂:

services:
  web:
    image: nginx:1.30-alpine
    networks: [frontend]
  app:
    build: .
    networks: [frontend, backend]
  db:
    image: postgres:18
    networks: [backend]

networks:
  frontend:
  backend:

web 可以連到 app,但連不到 db。

profiles:選擇性啟動的服務

上面範例中的 adminer 設定了 profiles: [tools]:

$ docker compose config --services
db
app
$ docker compose --profile tools config --services
db
app
adminer

沒有指定 profile 時,adminer 不會被啟動;加上 --profile tools 才會。適合放管理工具、測試用的郵件伺服器等「需要時才開」的服務。

資源限制與其他常用設定

services:
  app:
    image: myorg/app:1.4.2
    container_name: app          # 固定容器名稱(之後無法 scale)
    user: "1000:1000"
    working_dir: /app
    mem_limit: 512m
    cpus: 1.5
    init: true                   # 等於 docker run --init
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    extra_hosts:
      - "host.docker.internal:host-gateway"

compose.override.yaml:合併設定

Compose 會自動合併 compose.yaml 和 compose.override.yaml。範例的資料夾中還有這個檔案:

# compose.override.yaml
services:
  db:
    ports:
      - "127.0.0.1:5432:5432"

用 docker compose config 查看合併後的最終設定(注意最後的 ports):

$ docker compose config db
name: shop
services:
  db:
    environment:
      POSTGRES_DB: shop
      POSTGRES_PASSWORD: change-me
      POSTGRES_USER: shop
    image: postgres:18
    networks:
      default: null
    volumes:
      - type: volume
        source: pgdata
        target: /var/lib/postgresql
        volume: {}
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -h 127.0.0.1 -U shop -d shop
      timeout: 3s
      interval: 2s
      retries: 20
    ports:
      - mode: ingress
        host_ip: 127.0.0.1
        target: 5432
        published: "5432"
        protocol: tcp
networks:
  default:
    name: shop_default
volumes:
  pgdata:
    name: shop_pgdata

用 -f compose.yaml 明確只指定一個檔案時,就不會合併 override,ports 也就不見了:

$ docker compose -f compose.yaml config db
name: shop
services:
  db:
    environment:
      POSTGRES_DB: shop
      POSTGRES_PASSWORD: change-me
      POSTGRES_USER: shop
    image: postgres:18
    networks:
      default: null
    volumes:
      - type: volume
        source: pgdata
        target: /var/lib/postgresql
        volume: {}
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -h 127.0.0.1 -U shop -d shop
      timeout: 3s
      interval: 2s
      retries: 20
networks:
  default:
    name: shop_default
volumes:
  pgdata:
    name: shop_pgdata

docker compose config 也把簡寫展開成完整格式(例如 pgdata:/var/lib/postgresql 變成 type: volume、source、target),並完成 ${DB_PASSWORD} 的替換。設定看起來沒生效時,第一步就是用它確認 Compose 實際讀到的內容。

常見的分工:

  • compose.yaml:所有環境共用的設定。
  • compose.override.yaml:開發專用(掛載原始碼、開放除錯連接埠),本機執行 docker compose up 時自動套用。
  • compose.prod.yaml:正式環境,用 docker compose -f compose.yaml -f compose.prod.yaml up -d 指定。

重點回顧

  • image 使用現成映像檔,build 從 Dockerfile 建置。
  • depends_on + condition: service_healthy 才會等服務真正就緒。
  • profiles 讓管理工具等服務只在需要時啟動。
  • compose.override.yaml 會自動合併,用 docker compose config 檢查最終設定。

動手練習

練習 1:為本章的 db 服務加上 restart: unless-stopped,並限制記憶體 512 MB。

答案
  db:
    image: postgres:18
    restart: unless-stopped
    mem_limit: 512m

修改後執行 docker compose up -d 套用。

練習 2:adminer 設定了 profiles: [tools],怎麼一起啟動它?

答案
docker compose --profile tools up -d

常見問題

depends_on 為什麼沒有等資料庫準備好?

只寫 depends_on: [db] 只保證啟動順序。要等資料庫可以連線,需要 healthcheck 加上 condition: service_healthy,健康檢查的寫法見本章與 [PostgreSQL](/zh-tw/docker/postgresql/)。

YAML 縮排錯了會怎樣?

docker compose config 會報錯,例如 did not find expected key,見 [常見錯誤訊息](/zh-tw/docker/errors/)。只用空白,不要用 Tab。

version: '3.8' 還需要寫嗎?

不需要。Compose 規格已經不使用 version 欄位,新版會忽略它並顯示警告。

更新於 2026年10月14日