Skip to content

Compose File Format

This is a detailed reference for the Docker Compose file format. The Compose file defines services, networks, volumes, secrets, and configs for a Docker application.

File Discovery

Docker Compose looks for files in the following order:

  1. compose.yaml (preferred)
  2. compose.yml
  3. docker-compose.yaml
  4. docker-compose.yml

Override files are merged automatically:

  1. docker-compose.yml + docker-compose.override.yml
  2. Or specify explicitly: docker compose -f base.yml -f override.yml up

Top-Level Elements

yaml
# Optional: Name of the project
name: my-application

# Service definitions (required)
services:
  # ...

# Network definitions (optional)
networks:
  # ...

# Volume definitions (optional)
volumes:
  # ...

# Secret definitions (optional)
secrets:
  # ...

# Config definitions (optional)
configs:
  # ...

Services Reference

Complete Service Options

yaml
services:
  my-service:
    # ─── Image / Build ─────────────────────────
    image: nginx:1.25                    # Pre-built image
    build:                               # Build from source
      context: .                         # Build context path
      dockerfile: Dockerfile             # Dockerfile path
      args:                              # Build arguments
        NODE_ENV: production
      target: production                 # Multi-stage target
      cache_from:                        # Cache sources
        - type=registry,ref=cache:latest
      cache_to:                          # Cache destinations
        - type=registry,ref=cache:latest
      platforms:                         # Target platforms
        - linux/amd64
        - linux/arm64
      labels:                            # Build labels
        com.example.version: "1.0"
      no_cache: false                    # Disable cache
      pull: false                        # Always pull base

    # ─── Container Config ──────────────────────
    container_name: my-container         # Fixed name
    hostname: my-host                    # Hostname
    domainname: example.com              # Domain
    command: ["node", "server.js"]       # Override CMD
    entrypoint: ["docker-entrypoint.sh"] # Override ENTRYPOINT
    working_dir: /app                    # Working directory
    user: "1000:1000"                    # User:Group
    init: true                           # Use init process
    stdin_open: true                     # Keep STDIN open
    tty: true                            # Allocate TTY
    platform: linux/amd64               # Target platform
    pull_policy: always                  # Image pull policy
    # pull_policy: always | missing | never | build

    # ─── Networking ────────────────────────────
    ports:
      - "80:80"                          # host:container
      - "127.0.0.1:443:443"            # Localhost only
      - target: 3000                     # Long syntax
        published: "3000"
        protocol: tcp
        mode: host
    expose:
      - "3000"                           # Expose to linked services
    networks:
      frontend:
        aliases:
          - web
        ipv4_address: 172.28.0.10
      backend:
    network_mode: host                   # host | bridge | none
    dns:
      - 8.8.8.8
      - 8.8.4.4
    dns_search:
      - example.com
    extra_hosts:
      - "host.docker.internal:host-gateway"
      - "myhost:192.168.1.100"

    # ─── Storage ───────────────────────────────
    volumes:
      - data:/app/data                   # Named volume
      - ./src:/app/src                   # Bind mount
      - ./config:/app/config:ro          # Read-only
      - type: volume                     # Long syntax
        source: data
        target: /app/data
        read_only: false
      - type: bind
        source: ./logs
        target: /app/logs
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 100000000
    tmpfs:
      - /tmp
      - /var/run

    # ─── Environment ──────────────────────────
    environment:
      NODE_ENV: production
      DB_HOST: db
    env_file:
      - .env
      - path: .env.local
        required: false

    # ─── Dependencies ──────────────────────────
    depends_on:
      db:
        condition: service_healthy
        restart: true
      redis:
        condition: service_started
      migrations:
        condition: service_completed_successfully

    # ─── Health Check ──────────────────────────
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
      start_interval: 5s
      disable: false

    # ─── Restart Policy ────────────────────────
    restart: unless-stopped
    # restart: "no" | always | on-failure | unless-stopped

    # ─── Resources ─────────────────────────────
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 1G
          pids: 200
        reservations:
          cpus: '0.5'
          memory: 256M
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      rollback_config:
        parallelism: 1
        delay: 5s
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      placement:
        constraints:
          - node.role == worker

    # ─── Logging ───────────────────────────────
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
        compress: "true"

    # ─── Security ──────────────────────────────
    security_opt:
      - no-new-privileges:true
      - seccomp:custom.json
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    privileged: false

    # ─── Resource Limits ───────────────────────
    ulimits:
      nproc: 65535
      nofile:
        soft: 20000
        hard: 40000
    mem_limit: 512m
    mem_reservation: 256m
    cpus: 1.5
    cpu_shares: 512
    pids_limit: 100
    shm_size: '128m'

    # ─── Secrets & Configs ─────────────────────
    secrets:
      - db_password
      - source: api_key
        target: /run/secrets/api_key
        uid: '103'
        gid: '103'
        mode: 0440
    configs:
      - source: nginx_conf
        target: /etc/nginx/nginx.conf

    # ─── Labels ────────────────────────────────
    labels:
      com.example.project: "myapp"
      com.example.environment: "production"

    # ─── Lifecycle ─────────────────────────────
    stop_grace_period: 30s
    stop_signal: SIGTERM

    # ─── Profiles ──────────────────────────────
    profiles:
      - debug
      - development

    # ─── Extensions ────────────────────────────
    x-custom-label: "value"

Networks Reference

yaml
networks:
  # Simple network (Docker manages settings)
  default:

  # Bridge network with configuration
  frontend:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: frontend-br
    ipam:
      driver: default
      config:
        - subnet: 172.28.0.0/16
          ip_range: 172.28.5.0/24
          gateway: 172.28.0.1

  # Internal network (no external access)
  backend:
    driver: bridge
    internal: true

  # External network (pre-existing)
  existing-network:
    external: true
    name: my-existing-network

  # Network with labels
  labeled:
    labels:
      com.example.project: "myapp"

  # Overlay network (Swarm mode)
  swarm-net:
    driver: overlay
    attachable: true

Volumes Reference

yaml
volumes:
  # Simple volume
  data:

  # Volume with driver
  db-data:
    driver: local

  # Volume with driver options
  nfs-data:
    driver: local
    driver_opts:
      type: nfs
      o: "addr=192.168.1.100,nolock,soft,rw"
      device: ":/exports/data"

  # Bind mount as volume
  host-data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /srv/data

  # External volume
  shared:
    external: true
    name: my-shared-volume

  # Volume with labels
  logs:
    labels:
      com.example.description: "Log storage"

Secrets Reference

yaml
secrets:
  # File-based secret
  db_password:
    file: ./secrets/db_password.txt

  # Environment variable secret
  api_key:
    environment: API_KEY

  # External secret (Swarm mode)
  external_secret:
    external: true
    name: my-external-secret

Configs Reference

yaml
configs:
  # File-based config
  nginx_conf:
    file: ./nginx/nginx.conf

  # Content-based config
  app_config:
    content: |
      server.port=8080
      server.host=0.0.0.0

  # External config
  external_config:
    external: true
    name: my-external-config

Variable Substitution

yaml
services:
  web:
    image: nginx:${NGINX_VERSION:-1.25}
    ports:
      - "${HOST_PORT:-80}:80"
    environment:
      - DB_HOST=${DB_HOST:?DB_HOST must be set}
SyntaxBehavior
${VAR}Value of VAR, empty if unset
${VAR:-default}Value of VAR, or default if unset or empty
${VAR-default}Value of VAR, or default if unset
${VAR:?error}Value of VAR, or error if unset or empty
${VAR?error}Value of VAR, or error if unset
${VAR:+replacement}replacement if VAR is set and non-empty, else empty
${VAR+replacement}replacement if VAR is set (even if empty), else empty

Extensions (x- prefix)

Custom fields for reuse via YAML anchors:

yaml
x-common-env: &common-env
  LOG_LEVEL: info
  TZ: UTC

x-common-deploy: &common-deploy
  resources:
    limits:
      cpus: '0.5'
      memory: 256M
    reservations:
      cpus: '0.25'
      memory: 128M

x-common-healthcheck: &common-healthcheck
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 30s

services:
  api:
    image: my-api:latest
    environment:
      <<: *common-env
      API_PORT: "3000"
    deploy:
      <<: *common-deploy
    healthcheck:
      <<: *common-healthcheck
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]

  worker:
    image: my-worker:latest
    environment:
      <<: *common-env
      WORKER_CONCURRENCY: "4"
    deploy:
      <<: *common-deploy
    healthcheck:
      <<: *common-healthcheck
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]

Profiles

Profiles allow selective service startup:

yaml
services:
  web:
    image: nginx:latest
    # No profile: always starts

  api:
    image: my-api:latest
    # No profile: always starts

  debug:
    image: nicolaka/netshoot
    profiles:
      - debug

  test:
    image: my-test:latest
    profiles:
      - test

  monitoring:
    image: prom/prometheus
    profiles:
      - monitoring
      - production
bash
# Start only default services
docker compose up -d

# Start with debug profile
docker compose --profile debug up -d

# Start with multiple profiles
docker compose --profile debug --profile monitoring up -d

Merge and Override

bash
# Automatic merge (base + override)
docker compose up  # Merges docker-compose.yml + docker-compose.override.yml

# Explicit merge
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Multiple overrides (applied in order)
docker compose \
  -f docker-compose.yml \
  -f docker-compose.prod.yml \
  -f docker-compose.monitoring.yml \
  up -d

Merge Rules

FieldMerge Behavior
Single values (image, command)Override replaces
Maps (environment, labels)Deep merge
Lists (ports, volumes)Append
depends_onDeep merge
deployDeep merge

Validation

bash
# Validate and display resolved config
docker compose config

# Output as JSON
docker compose config --format json

# Validate specific files
docker compose -f docker-compose.yml -f docker-compose.prod.yml config

# Show only services
docker compose config --services

# Show only volumes
docker compose config --volumes

Next Steps

基于 MIT 许可发布