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 composeCLI; 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 -dThe 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 pullProfiles: 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 -dWatch 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/configdocker 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_passwordOverride 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 -dProduction 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_passwordRegular 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
- Compose secrets ↗
- Compose watch ↗
- Compose startup order ↗
- Compose down ↗
- Compose develop version requirements ↗
Adapted from technical notes. Examples use generic names.
Continue along a reading path
Docker: from running containers to application orchestration
- 01Docker basics: images, containers, and the daemon
- 02The Docker command line: images, containers, volumes, and networks
- 03Dockerfiles and image builds
- 04Compose: defining multi-service applications
- 05Docker networking and storage
- 06Docker in production: resources, security, and recovery
- 07Declaring Docker and containers on NixOS
- 08Swarm: from service replicas to cluster orchestration
- 09Docker and Podman: architecture, compatibility, and coexistence