目录

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 会把某些 数字:数字 格式解读成六十进制数,导致奇怪的错误。

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/docker/postgresql/)。

YAML 缩进错误会怎样?

docker compose config 会报错,例如 did not find expected key,详见[常见错误信息](/zh/docker/errors/)。只能使用空格,不能使用 Tab。

还需要写 version: '3.8' 吗?

不需要。Compose 规范已经不再使用 version 字段,新版会忽略它并显示警告。

更新于 2026年10月14日