目錄
compose.yaml 語法完整說明
compose.yaml 最上層主要有四個區塊:
name: myapp # 專案名稱(選用,預設是資料夾名稱)
services: # 要執行的容器
...
volumes: # 具名 Volume
...
networks: # 自訂網路(選用,預設會自動建立一個)
...
下面依照用途說明 services 底下最常用的設定,最後用一個完整的例子實際執行。
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日