Docker Compose — Docker Compose FAQ

3 minute read

Answers to the most frequently asked questions about Docker Compose.

General

What is the difference between Docker Compose V1 and V2?

Docker Compose V1 was a Python-based tool installed as docker-compose (with a hyphen). Docker Compose V2 is a Go-based plugin installed as docker compose (with a space). V2 is the current standard and is included with Docker Desktop.

# V1 (deprecated)
docker-compose up -d

# V2 (current)
docker compose up -d

Recommendation: Always use V2. Docker officially deprecated V1 in April 2023.

Should I use docker-compose.yml or compose.yml?

Both work, but compose.yml is the modern convention. Docker Compose V2 looks for files in this order:

  1. compose.yaml
  2. compose.yml
  3. docker-compose.yaml
  4. docker-compose.yml

Can I use Compose in production?

Yes, with caveats. Compose works well for single-host production deployments. For multi-host setups, consider Kubernetes or Docker Swarm. Use docker compose up -d with restart policies for reliable production operation.


Configuration

How do I use environment variables in compose.yml?

Three ways:

services:
  app:
    # 1. Direct value
    environment:
      DB_HOST: postgres

    # 2. Variable substitution from .env or shell
    environment:
      DB_HOST: ${DB_HOST}

    # 3. env_file
    env_file:
      - .env

How do I use multiple Compose files?

# Merge files (later files override earlier)
docker compose -f compose.yml -f compose.prod.yml up -d

# Or set COMPOSE_FILE environment variable
export COMPOSE_FILE=compose.yml:compose.prod.yml
docker compose up -d

How do I share compose.yml variables between services?

Use YAML anchors:

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

services:
  api:
    environment:
      <<: *common-env
      PORT: 3000

  worker:
    environment:
      <<: *common-env
      QUEUE: default

Networking

Can services communicate by name?

Yes. Compose creates a default network where services can reach each other using their service name as a hostname:

services:
  api:
    environment:
      DB_HOST: postgres  # ← this resolves to the postgres container
  postgres:
    image: postgres:16

How do I expose a service only to other containers?

Use expose instead of ports:

services:
  db:
    expose:
      - "5432"  # accessible to other containers, not from host

Volumes

What is the difference between named volumes and bind mounts?

services:
  app:
    volumes:
      - db-data:/var/lib/data     # Named volume — Docker manages storage
      - ./config:/etc/app/config  # Bind mount — maps a host directory

volumes:
  db-data:  # Named volume declaration

Named volumes persist across container restarts and are managed by Docker. Bind mounts map a specific host path into the container.

Does docker compose down delete my data?

No — docker compose down removes containers and networks but preserves volumes. To also delete volumes:

docker compose down -v  # WARNING: deletes all named volumes

Troubleshooting

Why is my service not starting?

Check in this order:

  1. docker compose ps — Is the container running?
  2. docker compose logs <service> — Any error messages?
  3. docker compose config — Is the YAML valid?
  4. Check depends_on with health checks — Are dependencies ready?

Why can one service not connect to another?

Common causes:

  1. Wrong hostname — Use the service name, not localhost
  2. Different networks — Both services must be on the same network
  3. Service not ready — Use depends_on with condition: service_healthy
  4. Wrong port — Use the container port, not the host port

How do I reset everything?

# Nuclear option — remove everything
docker compose down -v --rmi all --remove-orphans

# Verify nothing is left
docker compose ps -a

Performance

How do I limit container resources?

services:
  app:
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
        reservations:
          cpus: "0.25"
          memory: 128M

How do I speed up builds?

  1. Use multi-stage builds to reduce image size
  2. Order Dockerfile layers — put COPY package*.json before COPY . .
  3. Use .dockerignore to exclude unnecessary files
  4. Use BuildKit — DOCKER_BUILDKIT=1 docker compose build

Best Practices

Should every project have a compose.yml?

Yes. Even a single-container app benefits from a compose.yml:

  • Documents the runtime configuration
  • Makes onboarding easier (docker compose up and done)
  • Enables easy addition of databases, caches, etc.

How should I organize multiple compose files?

project/
├── compose.yml           # Base configuration
├── compose.dev.yml       # Development overrides
├── compose.prod.yml      # Production overrides
├── compose.test.yml      # Test-specific services
└── .env                  # Default environment variables

Should I pin image versions?

Always. Never use :latest in production:

# Bad
image: postgres:latest

# Good
image: postgres:16.1-alpine

Pinning versions prevents unexpected breakage when images are updated.