OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 113

Compose: defining multi-service applications

Manage single-host applications with services, dependencies, profiles, watch, and secrets.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSDocker: from running containers to application orchestration ↗
About 5 min readStart reading

Define multiple containers in a single YAML file and start the entire application stack with one command. This article uses the Compose v2-style docker compose CLI; individual profiles/watch/develop features require the corresponding versions, and third-party compatible implementations must be checked feature by feature.


What is Compose?#

# 没有 Compose:手动逐条启动
docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD_FILE=/run/secrets/db_password --mount type=bind,src=/path/to/protected-password-file,dst=/run/secrets/db_password,readonly -v pgdata:/var/lib/postgresql/data postgres:16-alpine
docker run -d --name api --network appnet -e DB_HOST=db -p 3000:3000 myapi
docker run -d --name web -p 80:80 --network appnet nginx

# 有 Compose:一个文件 + 一个命令
docker compose up -d

The structure of compose.yaml#

# compose.yaml(默认文件名)
# 注意:v2 不再需要 version: 字段!

services:                    # 所有服务定义
  db:
    image: postgres:16-alpine
    restart: always
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD:?provide a local database password}   # 插值示意;生产考虑 secret 文件
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10
      start_period: 30s

  api:
    build: ./api              # 从 ./api/Dockerfile 构建(而不是拉现成镜像)
    restart: always
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://postgres:${DB_PASSWORD}@db:5432/app
    depends_on:
      db:
        condition: service_healthy   # 等 db 健康检查通过后再启 api
    develop:                  # watch mode(compose v2.22+)
      watch:
        - action: sync
          path: ./api/src
          target: /app/src

  web:
    image: nginx:alpine
    restart: always
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - api

volumes:
  pgdata:                    # 声明命名卷

Common commands#

# 启动(构建 + 创建 + 启动所有服务)
docker compose up -d
#               └┘ 后台模式

# 启动并重新构建(代码改了需要重新 build)
docker compose up -d --build

# 只启动指定服务 + 依赖
docker compose up -d api

# 查看所有服务状态
docker compose ps

# 查看日志
docker compose logs -f        # 所有服务
docker compose logs -f api    # 只看 api

# 在运行中的服务里执行命令
docker compose exec api bash

# 停止所有服务
docker compose stop

# 停止并删除容器、网络(保留卷)
docker compose down

# 还删除声明的非 external 命名卷及匿名卷;先备份,external 卷不由 down 删除
docker compose down -v

# 重启单个服务
docker compose restart api

# 拉取所有服务的最新镜像
docker compose pull

Profiles: enable services for different scenarios#

Use the same compose file to enable different services as needed:

services:
  api:
    image: myapp:latest     # 没有 profiles → 总是启动

  db:
    image: postgres:16-alpine
    profiles: [full, production]  # 只在指定 profile 下启动

  redis:
    image: redis:alpine
    profiles: [production]

  pgadmin:
    image: dpage/pgadmin4
    profiles: [tools]           # 开发时才用的管理工具

  mailhog:
    image: mailhog/mailhog
    profiles: [dev]             # 开发时捕获邮件
# 默认只启动无 profile 的服务(api)
docker compose up -d

# 启动 full profile 下所有服务(api + db)
docker compose --profile full up -d

# 启动多个 profile
docker compose --profile dev --profile tools up -d

Watch Mode: automatic synchronization during development#

Watch is available in Compose v2.22+; sync+restart requires 2.23.0+. Check the corresponding documentation for other actions:

services:
  api:
    build: .
    develop:
      watch:
        - action: sync          # 文件变了 → 直接同步到容器(快)
          path: ./src
          target: /app/src

        - action: rebuild       # package.json 变了 → 重新构建镜像(慢)
          path: ./package.json  # rebuild 使用构建上下文,不需 target

        - action: sync+restart  # 配置文件变了 → 同步 + 重启容器
          path: ./config
          target: /app/config
docker compose up --watch
# 修改 ./src 代码 → 自动同步到容器
# 修改 package.json → 自动重新构建

Environment variables and Secrets#

The .env file

The following is a placeholder format that cannot be executed as written. Real credentials should come from controlled files or a secret-management tool; never put them in public examples.

# .env(放在 compose.yaml 同级目录,加入 .gitignore)
DB_PASSWORD=<从受控凭据来源提供>
API_KEY=<从受控凭据来源提供>
# compose.yaml 中引用
services:
  db:
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}   # 自动从 .env 读取

Compose file secrets and Swarm secrets

# 以下 external secret 由 Swarm 管理;普通 Compose 可改用 file: 来源
secrets:
  db_password:
    external: true       # 预先用 docker secret create 创建

services:
  db:
    secrets:
      - db_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password

Override files#

Manage multiple environments with a base file plus environment-specific override files.

# compose.yaml(基础配置)
services:
  api:
    image: myapp:latest
    ports:
      - "3000:3000"
  db:
    image: postgres:16-alpine
# compose.prod.yaml(生产环境覆盖)
services:
  api:
    restart: always
    deploy:
      resources:
        limits:
          memory: 512M
    environment:
      NODE_ENV: production

  db:
    restart: always
    volumes:
      - prod-pgdata:/var/lib/postgresql/data

volumes:
  prod-pgdata:
# 开发环境(只用基础文件)
docker compose up -d

# 生产环境(基础 + 覆盖)
docker compose -f compose.yaml -f compose.prod.yaml up -d

Production recommendations#

For detailed production practices, see Part Six: Production Deployment Practices. Key principles:

Principle Practice
Avoid the latest tag Pin a version or digest
Choose an appropriate restart policy Handle exits; an unhealthy state on a single host does not automatically trigger a restart
Configure health checks Use with depends_on condition: service_healthy
Limit resources deploy.resources.limits
Use override files to manage environments -f compose.yaml -f compose.prod.yaml
Review before using --remove-orphans It deletes service containers in this project that are no longer declared; confirm the data and scope first

Regular Compose supports secrets too

secrets:
  db_password:
    file: ./protected/db_password.txt
services:
  db:
    image: postgres:16-alpine
    secrets: [db_password]
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password

Regular Compose mounts secrets from controlled source files; it does not automatically gain Swarm's Raft encryption and distribution mechanisms. Protect the source files and deployment permissions. .env is used for interpolation; it does not automatically inject every variable into containers and is not encrypted credential storage. The output of docker compose config may contain interpolated secrets, so avoid publishing it.

Startup and dependency boundaries

The short form of depends_on orders startup only; it does not guarantee application readiness. A health condition waits for a successful check but does not continually guarantee that the dependency stays healthy afterward. Connection retries and failure handling remain the application's responsibility. Services without a profile are enabled by default. Explicitly targeting a service with a profile starts that service and its required dependencies, but does not automatically enable every other service in the same profile. Services with fixed host ports cannot simply be scaled to multiple replicas.

The profiles, override, and secret sections are independent configuration fragments. Before they can run, combine them with complete service settings for images, database credentials, health-check tools, and ports. This is not a complete project that has been tested at runtime.

References & further reading

  1. Compose secrets ↗
  2. Compose watch ↗
  3. Compose startup order ↗
  4. Compose down ↗
  5. Compose develop version requirements ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Docker: from running containers to application orchestration

  1. 01Docker basics: images, containers, and the daemon
  2. 02The Docker command line: images, containers, volumes, and networks
  3. 03Dockerfiles and image builds
  4. 04Compose: defining multi-service applications
  5. 05Docker networking and storage
  6. 06Docker in production: resources, security, and recovery
  7. 07Declaring Docker and containers on NixOS
  8. 08Swarm: from service replicas to cluster orchestration
  9. 09Docker and Podman: architecture, compatibility, and coexistence
CONTINUE READING / NEXT ARTICLEDocker networking and storage

Choose network modes and persistence methods, with clear port-exposure and data-lifecycle boundaries.