---
title: "Docker Compose"
description: "Comprehensive Docker Compose reference guide covering services, volumes, networks, ports, environment variables, commands, configurations, and container orchestration best practices."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/cheatsheets/docker-compose
---

# Docker Compose

Docker Compose is a tool for defining and running multi-container Docker applications. It uses a YAML file to configure application services, networks, and volumes. This cheatsheet provides a quick reference for common Docker Compose commands, configuration options, best practices, and troubleshooting tips.

## Getting Started

### Installation & Setup

Install Docker Compose and verify installation on your system

**Keywords:** installation, setup, binary, package manager

#### Install Docker Compose on Linux

```bash
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
```

_exec_
```bash
Docker Compose version v2.25.0
```

Downloads the Docker Compose binary for your system architecture and sets executable permissions

- Requires curl to be installed
- Verify checksum for security
- Use docker compose instead of docker-compose in v2

#### Verify Docker Compose Installation

```bash
docker compose version
docker compose --help
```

_exec_
```bash
Docker Compose version v2.25.0, build something
Usage: docker compose [OPTIONS] COMMAND

Options:
  --version  Show the version and exit
  -f, --file FILE  Specify path to compose file
  -p, --project-name STRING  Set project name
```

Check Docker Compose version and review available commands

- Docker Compose v2 onwards uses 'docker compose' command
- Legacy 'docker-compose' is deprecated

### Project Structure & Configuration

Set up a Docker Compose project with proper directory structure

**Keywords:** project structure, compose.yml, configuration, initialization

#### Create Docker Compose Project Directory

```bash
mkdir my-app
cd my-app
touch compose.yml
mkdir -p app/src
mkdir -p data
```

_exec_
```bash
$ tree my-app/
my-app/
├── compose.yml
├── app
│   └── src
└── data
```

Create a basic project structure for Docker Compose with necessary directories

- Keep compose.yml at project root
- Use consistent directory naming

#### Initialize with Example Compose File

```yaml
version: '3.9'
services:
  app:
    image: nginx:latest
    ports:
      - "80:80"
```

_exec_
```bash
$ docker compose config
name: my-app
services:
  app:
    image: 'nginx:latest'
    ports:
      - mode: ingress
        target: 80
        published: "80"
        protocol: tcp
version: '3.9'
```

Create minimal valid compose.yml and validate with config command

- Version '3.9' is latest v3 stable
- Always validate configuration

### Version & Compatibility

Understand Docker Compose versions and compatibility requirements

**Keywords:** version, compatibility, features, api version

#### Check Docker Compose and Docker Version

```bash
docker --version
docker compose version
docker compose version --format json
```

_exec_
```bash
Docker version 25.0.0, build abcdef12
Docker Compose version v2.25.0, build build123
{"Version":"v2.25.0","ApiVersion":"1.46","Experimental":false}
```

Verify both Docker Engine and Docker Compose versions are installed

- Docker Engine 20.10+ required for Compose v2
- Use JSON format for parsing in scripts

#### Version-Specific Features in Compose File

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
```

_exec_
```bash
$ docker compose up -d
[+] Running 1/1
 ✔ Container my-app-web-1  Started
```

Use version 3.9+ features like healthcheck for container monitoring

- Version 3.8+ supports properties
- Version 3.9 is feature-complete for most use cases

## Basic Services

### Service Definition & Images

Define services using images and configure basic service properties

**Keywords:** service, image, container, definition

#### Define Single Service with Image

```yaml
version: '3.9'
services:
  web:
    image: nginx:1.24
    container_name: my-nginx
    restart: always
```

_exec_
```bash
$ docker compose up -d
[+] Running 1/1
 ✔ Container my-nginx  Created
```

Define a service using pre-built image with container naming and restart policy

- Image format: name:tag or name:digest
- container_name overrides auto-generated name

#### Multiple Services from Different Images

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    container_name: web-server
  db:
    image: postgres:15
    container_name: database
  cache:
    image: redis:7-alpine
    container_name: redis-cache
```

_exec_
```bash
$ docker compose up -d
[+] Running 3/3
 ✔ Container web-server     Created
 ✔ Container database       Created
 ✔ Container redis-cache   Created
```

Define multiple interdependent services each using different official images

- Services communicate via service name automatically
- Alpine variants use less disk space

#### Service with Image Pull Configuration

```yaml
version: '3.9'
services:
  app:
    image: myregistry.azurecr.io/myapp:v1.0
    pull_policy: always
    container_name: app-container
```

_exec_
```bash
$ docker compose up
[+] Pulling 1/1
 ✔ myregistry.azurecr.io/myapp:v1.0 Pulled
[+] Running 1/1
 ✔ Container app-container  Created
```

Pull image from private registry and always fetch latest version

- pull_policy can be always, never, or missing
- Missing policy (default) only pulls if image not local

### Resource Constraints

Limit CPU and memory usage for services

**Keywords:** resource limits, memory, cpu, constraints

#### Set Memory and CPU Limits

```yaml
version: '3.9'
services:
  app:
    image: node:18
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
```

_exec_
```bash
$ docker compose up -d
$ docker stats
CONTAINER ID   NAME     CPU %    MEM USAGE / LIMIT
abc123def456   app-1    0.5%    128M / 512M
```

Set hard limits and soft reservations for container resources

- Limits prevent container from using more than specified
- Reservations guarantee minimum resources
- Format: 0.5 = 50% of one CPU

#### Cascading Resource Configuration

```yaml
version: '3.9'
services:
  database:
    image: mysql:8
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
  cache:
    image: redis:7
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
```

_exec_
```bash
$ docker compose stats
NAME       CPU %   MEM USAGE / LIMIT
database   0.8%    512M / 1G
cache      0.2%    45M / 256M
```

Apply different resource constraints to different services

- Match resources to service requirements
- Monitor actual usage to adjust limits

### Restart Policies

Configure how containers restart on failure

**Keywords:** restart policy, failure, recovery, always

#### Configure Restart Policy

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    restart: always
  app:
    image: node:18
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
        max_attempts: 3
        delay: 5s
```

_exec_
```bash
$ docker compose up -d
[+] Running 2/2
 ✔ Container web-1  Started
 ✔ Container app-1  Started
```

Set restart policies to keep containers running or retry on failure

- always: always restart unless explicitly stopped
- on-failure: restart only if exit code is non-zero
- max_attempts: limit retry count

#### Restart Policy with Exponential Backoff

```yaml
version: '3.9'
services:
  api:
    image: myapp:latest
    deploy:
      restart_policy:
        condition: on-failure
        max_attempts: 5
        delay: 1s
        window: 120s
```

_exec_
```bash
$ docker compose logs api
api-1 | Starting service...
api-1 | Service started
```

Retry with limited attempts and timeout window using deploy configuration

- window: only count failures within this timeframe
- Prevents infinite restart loops

## Ports & Networking

### Port Mapping & Exposure

Map container ports to host ports and expose services

**Keywords:** port mapping, exposure, port, expose

#### Basic Port Mapping

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
      - "8443:443"
```

_exec_
```bash
$ docker compose up -d
$ curl http://localhost:8080
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title></head>
```

Map host port 8080 to container port 80, and 8443 to 443

- Format: host:container
- Can specify IP: 127.0.0.1:8080:80

#### Multiple Services with Port Mapping

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
  api:
    image: node:18
    ports:
      - "3000:3000"
  db:
    image: postgres:15
    ports:
      - "5432:5432"
```

_exec_
```bash
$ docker compose ps
NAME      IMAGE          PORTS
web-1     nginx:latest   0.0.0.0:80->80/tcp
api-1     node:18        0.0.0.0:3000->3000/tcp
db-1      postgres:15    0.0.0.0:5432->5432/tcp
```

Expose multiple services on different ports

- Each service can have multiple port mappings
- Default protocol is tcp, can specify udp

#### Port Mapping with Protocol Specification

```yaml
version: '3.9'
services:
  dns:
    image: coredns:latest
    ports:
      - "53:53/udp"
      - "53:53/tcp"
  web:
    image: nginx:latest
    ports:
      - target: 80
        published: 8080
        protocol: tcp
```

_exec_
```bash
$ docker compose ps
NAME    IMAGE               PORTS
dns-1   coredns:latest      0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp
web-1   nginx:latest        0.0.0.0:8080->80/tcp
```

Specify protocol (tcp/udp) and use verbose port syntax

- Default protocol is tcp
- Verbose syntax allows more control

### Network Configuration

Configure custom networks and manage service connectivity

**Keywords:** networks, custom network, bridge, connectivity

#### Define Custom Networks

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    networks:
      - frontend
  api:
    image: node:18
    networks:
      - frontend
      - backend
  db:
    image: postgres:15
    networks:
      - backend
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
```

_exec_
```bash
$ docker compose up -d
[+] Creating network my-app_frontend
[+] Creating network my-app_backend
$ docker network ls
NETWORK ID    NAME               DRIVER
abc123        my-app_frontend    bridge
def456        my-app_backend     bridge
```

Define custom networks and assign services to them for isolation

- Services on same network can communicate via service name
- Services on different networks require linking

#### External Network Integration

```yaml
version: '3.9'
services:
  app:
    image: node:18
    networks:
      - shared-network
networks:
  shared-network:
    external: true
    name: production-network
```

_exec_
```bash
$ docker compose up -d
[+] Running 1/1
 ✔ Container app-1  Started
$ docker network inspect production-network
[
  {
    "Name": "production-network",
    "Containers": {
      "abc123...": {
        "Name": "app-1"
      }
    }
  }
]
```

Connect services to external networks created outside Compose

- External networks must exist before docker compose up
- Useful for multi-compose-file deployments

### DNS & Service Discovery

Configure DNS and internal service discovery

**Keywords:** dns, service discovery, hostname, resolution

#### Service DNS Resolution

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    hostname: webserver
  api:
    image: node:18
    hostname: apiserver
```

_exec_
```bash
$ docker compose exec api curl http://webserver
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title></head>
```

Services can discover each other using service name or hostname

- Default: service name is used for DNS
- Hostname overrides service name if specified

#### Custom DNS Servers

```yaml
version: '3.9'
services:
  app:
    image: node:18
    dns:
      - 8.8.8.8
      - 8.8.4.4
    dns_search:
      - example.com
      - internal.local
```

_exec_
```bash
$ docker compose exec app cat /etc/resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4
search example.com internal.local
```

Override default DNS servers and search domains

- Useful for internal DNS or custom resolvers
- Multiple servers provide redundancy

## Volumes & Storage

### Volume Types & Mounting

Work with named volumes, anonymous volumes, and bind mounts

**Keywords:** volumes, named volume, bind mount, storage

#### Named Volumes for Data Persistence

```yaml
version: '3.9'
services:
  db:
    image: postgres:15
    volumes:
      - db_data:/var/lib/postgresql/data
  cache:
    image: redis:7
    volumes:
      - cache_data:/data
volumes:
  db_data:
  cache_data:
```

_exec_
```bash
$ docker compose up -d
$ docker volume ls
DRIVER    VOLUME NAME
local     my-app_db_data
local     my-app_cache_data
```

Create named volumes for persistent data that survives container removal

- Named volumes are managed by Docker
- Data persists when containers are removed
- Can be used across containers

#### Bind Mounts for Development

```yaml
version: '3.9'
services:
  app:
    image: node:18
    working_dir: /app
    volumes:
      - ./src:/app/src
      - ./package.json:/app/package.json
      - ./package-lock.json:/app/package-lock.json
    ports:
      - "3000:3000"
```

_exec_
```bash
$ docker compose up -d
$ ls /app
src  package.json  package-lock.json
```

Mount local directories into container for live development

- Changes on host immediately visible in container
- Perfect for development workflows
- Use relative paths for portability

#### Anonymous Volumes and Read-Only Volumes

```yaml
version: '3.9'
services:
  app:
    image: node:18
    volumes:
      - /tmp
      - ./config.json:/app/config.json:ro
      - shared_data:/shared:rw
volumes:
  shared_data:
```

Use anonymous volumes, read-only mounts, and shared writable volumes

- Anonymous volumes are cleaned on down
- ro = read-only, rw = read-write
- :ro prevents accidental modifications

#### Volume Mount Options

```yaml
version: '3.9'
services:
  db:
    image: postgres:15
    volumes:
      - type: volume
        source: db_data
        target: /var/lib/postgresql/data
        volume:
          nocopy: true
      - type: bind
        source: ./init-scripts
        target: /docker-entrypoint-initdb.d
        read_only: true
volumes:
  db_data:
```

_exec_
```bash
$ docker compose up -d
$ docker compose exec db ls /var/lib/postgresql/data
base global pg_hba.conf
```

Use long-form volume syntax for granular control

- nocopy: don't copy volume content from existing data
- read_only: mount as read-only
- More explicit than shorthand

### Volume Management & Cleanup

Manage and maintain Docker Compose volumes

**Keywords:** volume management, cleanup, prune, removal

#### List and Inspect Volumes

```bash
docker compose volume ls
docker volume inspect my-app_db_data
docker volume inspect my-app_db_data --format='{{json .Mountpoint}}'
```

_exec_
```bash
DRIVER    VOLUME NAME
local     my-app_db_data

[
  {
    "Name": "my-app_db_data",
    "Driver": "local",
    "Mountpoint": "/var/lib/docker/volumes/my-app_db_data/_data"
  }
]
```

List volumes created by Compose and inspect their properties

- Volumes persist after docker compose down
- Mountpoint shows where data is stored

#### Clean Up Volumes

```bash
docker compose down -v
docker volume rm my-app_db_data
docker volume prune -f
```

_exec_
```bash
Removing stopped containers...
Removing named volumes...
Deleted Volumes:
my-app_db_data

Deleted Volumes:
my-app_cache_data
```

Remove volumes when stopping containers or clean unused volumes

- down -v removes named volumes declared in compose file
- prune removes all unused volumes
- Use force flag to skip confirmation

## Environment & Configuration

### Environment Variables

Set and manage environment variables for services

**Keywords:** environment variables, env vars, configuration, settings

#### Environment Variables in Compose File

```yaml
version: '3.9'
services:
  app:
    image: node:18
    environment:
      NODE_ENV: production
      LOG_LEVEL: debug
      DATABASE_URL: postgres://user:pass@db:5432/mydb
      API_PORT: 3000
```

_exec_
```bash
$ docker compose exec app env
NODE_ENV=production
LOG_LEVEL=debug
DATABASE_URL=postgres://user:pass@db:5432/mydb
API_PORT=3000
```

Define environment variables directly in compose.yml

- Variables are set when container starts
- Application code reads these variables

#### Environment Files (.env)

```yaml
version: '3.9'
services:
  db:
    image: postgres:15
    env_file:
      - .env.database
      - .env.common
  app:
    image: node:18
    env_file: .env
    environment:
      NODE_ENV: ${NODE_ENV}
```

_exec_
```bash
$ cat .env
NODE_ENV=production
API_KEY=sk-12345
$ docker compose up -d
$ docker compose exec app echo $NODE_ENV
production
```

Load environment variables from .env files and reference them in compose

- Files are loaded in order, later overrides earlier
- Use ${VAR} syntax to reference variables
- .env in current directory loaded automatically

#### Variable Substitution and Defaults

```yaml
version: '3.9'
services:
  web:
    image: nginx:${NGINX_VERSION:-latest}
    ports:
      - "${HOST_PORT:-80}:80"
  api:
    image: node:18
    environment:
      DATABASE_HOST: ${POSTGRES_HOST}
      DATABASE_PORT: ${POSTGRES_PORT:-5432}
      SECRET_KEY: ${SECRET_KEY}
```

_exec_
```bash
$ NGINX_VERSION=1.24 docker compose up
$ docker compose config --resolve-image-digests
version: '3.9'
services:
  web:
    image: 'nginx:1.24'
    ports:
      - mode: ingress
        target: 80
        published: "80"
```

Substitute variables with defaults using ${VAR:-default} syntax

- :- provides default if variable not set
- Define in .env or export before docker compose
- Use in any field

#### Per-Service Environment Variables

```yaml
version: '3.9'
services:
  app:
    image: node:18
    env_file: .env.app
    environment:
      NODE_ENV: ${NODE_ENV:-development}
  db:
    image: postgres:15
    env_file: .env.database
    environment:
      POSTGRES_DB: ${DB_NAME:-mydb}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
```

_exec_
```bash
$ docker compose exec db psql -U ${DB_USER} -d ${DB_NAME}
psql (15.1)
Type "help" for help.
```

Set different environment variables for each service with .env files

- Each service can have its own .env file
- Can mix env_file and environment directives

### Configuration Files & Secrets

Manage configuration files and sensitive data

**Keywords:** configuration, secrets, files, sensitive data

#### Mount Configuration Files as Volumes

```yaml
version: '3.9'
services:
  nginx:
    image: nginx:latest
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./conf.d:/etc/nginx/conf.d:ro
  app:
    image: node:18
    volumes:
      - ./config/app.json:/app/config/app.json:ro
```

_exec_
```bash
$ docker compose exec nginx cat /etc/nginx/nginx.conf
# nginx configuration
```

Mount configuration files into containers as read-only volumes

- :ro makes mount read-only
- Prevents accidental modifications

#### Use Environment Files for Secrets

```yaml
version: '3.9'
services:
  app:
    image: node:18
    env_file:
      - .env
      - .env.secrets
    environment:
      DATABASE_PASSWORD: ${DB_PASSWORD}
      API_KEY: ${API_KEY}
```

_exec_
```bash
$ cat .env.secrets
DB_PASSWORD=super_secret_password_123
API_KEY=sk-abc123def456
```

Store secrets in separate .env files and load them securely

- Add .env.secrets to .gitignore
- Keep secrets out of version control
- Consider using Docker secrets for production

## Build Configuration

### Build from Dockerfile

Build custom images from Dockerfile during compose up

**Keywords:** build, Dockerfile, custom image, build context

#### Basic Build Configuration

```yaml
version: '3.9'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: development
```

_exec_
```bash
$ docker compose build
[+] Building 12.3s (15/15) FINISHED
 => [internal] load build definition from Dockerfile
 => [stage-0 1/5] FROM node:18
 => [stage-0 2/5] WORKDIR /app
 => [stage-0 3/5] COPY package*.json ./
 => [stage-0 4/5] RUN npm ci
 => [stage-0 5/5] RUN npm run build
 => exporting to image
Successfully tagged my-app-app:latest
```

Build Docker image from Dockerfile in current directory

- build: . uses Dockerfile in current directory
- Image tagged as project_service:latest

#### Build with Specific Dockerfile and Context

```yaml
version: '3.9'
services:
  app:
    build:
      context: ./src
      dockerfile: Dockerfile.prod
      args:
        NODE_ENV: production
        BUILD_VERSION: 1.0.0
  api:
    build:
      context: ./api-service
      dockerfile: Dockerfile
```

_exec_
```bash
$ docker compose build
[+] Building 25.1s (20/20) FINISHED
 => [internal] load build definition from ./src/Dockerfile.prod
 => [stage-0 1/8] FROM node:18
 => Setting build args: NODE_ENV=production, BUILD_VERSION=1.0.0
Successfully tagged my-app-app:latest
```

Build from different Dockerfile with custom context and build arguments

- context: directory with Dockerfile
- dockerfile: specify non-standard Dockerfile name
- args: pass build-time arguments

#### Multi-Stage Build with Compose

```yaml
version: '3.9'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.multistage
      target: runtime
      args:
        PYTHON_VERSION: 3.11
```

_exec_
```bash
$ docker compose build
[+] Building 15.2s (25/25) FINISHED
 => [builder 1/8] FROM python:3.11
 => [builder 2/8] WORKDIR /build
 => [builder 3/8] COPY . .
 => [builder 4/8] RUN pip install -r requirements.txt
 => [runtime 1/3] FROM python:3.11-slim
 => [runtime 2/3] COPY --from=builder /build/app /app
Successfully tagged my-app-app:latest
```

Use multi-stage build to create smaller final images

- target: build specific stage
- Copy artifacts from builder stage to reduce size

### Build Arguments and Cache

Pass arguments to builds and manage build cache

**Keywords:** build args, cache, no-cache, build-time

#### Build Arguments in Dockerfile

```yaml
version: '3.9'
services:
  app:
    build:
      context: .
      args:
        - BUILD_DATE=2026-02-28
        - VCS_REF=abc123def456
        - VERSION=1.0.0
```

_exec_
```bash
$ docker compose build
[+] Building 10.5s (8/8) FINISHED
 => [internal] load build definition
 => [stage-0 1/3] FROM node:18
 => [stage-0 2/3] RUN echo "Version: 1.0.0"
 => [stage-0 3/3] RUN echo "Built: 2026-02-28"
```

Pass build-time arguments that become available as ARG in Dockerfile

- Args are passed during build phase only
- Use ARG instruction in Dockerfile
- Environment-specific configuration

#### Cache Management

```bash
docker compose build --no-cache
docker compose build --no-cache app
docker compose build --pull
```

_exec_
```bash
$ docker compose build --no-cache
[+] Building 30.2s (15/15) FINISHED
 => [stage-0 1/5] FROM node:18
 => [stage-0 2/5] WORKDIR /app
 => [stage-0 3/5] COPY . .  [cached]
 => [stage-0 4/5] RUN npm ci [not cached]
Successfully tagged my-app-app:latest
```

Skip Docker layer cache or force rebuilding with fresh images

- --no-cache: rebuild all layers
- --pull: always pull base images
- Useful when dependencies change

## Advanced Features

### Health Checks & Monitoring

Configure health checks to monitor container status

**Keywords:** healthcheck, monitoring, liveness, readiness

#### Implement Health Checks

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
  api:
    image: node:18
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
```

_exec_
```bash
$ docker compose up -d
$ docker compose ps
NAME    IMAGE         STATUS
web-1   nginx:latest  Up 30s (health: starting)
api-1   node:18       Up 25s (health: healthy)
```

Define health checks to verify container is running correctly

- test: command to run (CMD or CMD-SHELL)
- start_period: grace period before health checks
- Exit code 0 = healthy, non-zero = unhealthy

#### Depends On with Health Checks

```yaml
version: '3.9'
services:
  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
  app:
    image: node:18
    depends_on:
      db:
        condition: service_healthy
```

_exec_
```bash
$ docker compose up
db-1 | PostgreSQL accepting connections
app-1 | Waiting for service_healthy condition on db
```

Wait for dependent service to pass health check before starting

- condition: service_healthy waits for passing health check
- Prevents startup order issues

### Service Dependencies

Define startup order and dependencies between services

**Keywords:** depends_on, startup order, dependencies, conditions

#### Basic Service Dependencies

```yaml
version: '3.9'
services:
  db:
    image: postgres:15
  cache:
    image: redis:7
  app:
    image: node:18
    depends_on:
      - db
      - cache
```

_exec_
```bash
$ docker compose up
[+] Running 3/3
 ✔ Container pg-1     Started
 ✔ Container redis-1  Started
 ✔ Container app-1    Started
```

Specify that app depends on db and cache services

- Services listed as dependencies start first
- Does not wait for service to be ready, only for container to start

#### Conditional Dependency with Service Ready

```yaml
version: '3.9'
services:
  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
  api:
    image: node:18
    depends_on:
      db:
        condition: service_healthy
```

_exec_
```bash
$ docker compose up -d
db-1 | PostgreSQL started
api-1 waiting for db...
api-1 | Connected to database
```

Wait for service to be healthy before starting dependent service

- Requires health check on dependency
- Prevents connection errors at startup

### Labels & Metadata

Add metadata labels to containers and services

**Keywords:** labels, metadata, tagging, organization

#### Add Labels to Services

```yaml
version: '3.9'
services:
  web:
    image: nginx:latest
    labels:
      com.example.description: "Web server"
      com.example.version: "1.0"
      com.example.tier: "frontend"
  app:
    image: node:18
    labels:
      com.example.description: "API server"
      com.example.version: "2.0"
      com.example.tier: "backend"
```

_exec_
```bash
$ docker inspect my-app-web-1 --format='{{json .Config.Labels}}'
{"com.example.description":"Web server","com.example.tier":"frontend","com.example.version":"1.0"}
```

Attach metadata labels to services for organization and tooling

- Use reverse domain notation for label keys
- Can be inspected with docker inspect
- Used by monitoring and orchestration tools

### Override Entrypoint & Command

Override default container command and entrypoint

**Keywords:** entrypoint, command, override, cmd

#### Override Command

```yaml
version: '3.9'
services:
  app:
    image: node:18
    working_dir: /app
    command: npm start
    volumes:
      - ./app:/app
  worker:
    image: node:18
    working_dir: /app
    command: npm run worker
    volumes:
      - ./app:/app
```

_exec_
```bash
$ docker compose up -d
app-1 | npm start
app-1 | Server listening on port 3000
```

Override default CMD with custom commands

- command: overrides CMD from Dockerfile
- Each service can have different command

#### Override Entrypoint

```yaml
version: '3.9'
services:
  app:
    image: myapp:latest
    entrypoint: /bin/bash
    command: -c "npm install && npm start"
```

_exec_
```bash
$ docker compose up
app-1 | /bin/bash -c npm install && npm start
```

Completely override container entrypoint

- entrypoint: completely replaces ENTRYPOINT from Dockerfile
- Use for different execution paths

## Docker Compose Commands

### Lifecycle Management (up/down/stop)

Start, stop, and manage container lifecycle

**Keywords:** up, down, stop, start, restart

#### Start Services (docker compose up)

```bash
docker compose up
docker compose up -d
docker compose up --build
docker compose up -f compose.yml -f docker-compose.prod.yml
```

_exec_
```bash
[+] Running 3/3
 ✔ Container pg-1      Started
 ✔ Container redis-1   Started
 ✔ Container app-1     Started
```

Start services defined in compose file

- up: creates and starts services
- -d: detached mode (background)
- --build: build before starting

#### Stop and Remove Services (docker compose down)

```bash
docker compose stop
docker compose down
docker compose down -v
docker compose down --rmi all
```

_exec_
```bash
Stopping my-app-app-1 ... done
Removing my-app-app-1 ... done
Removing network my-app_default
```

Stop services or remove containers, networks, and optionally volumes

- stop: stops containers (can restart)
- down: removes containers, networks
- -v: also remove named volumes
- --rmi all: remove created images

#### Pause and Resume Services

```bash
docker compose pause
docker compose unpause
docker compose kill
```

_exec_
```bash
Pausing my-app-app-1 ... done
Unpausing my-app-app-1 ... done
Killing my-app-app-1 ... done
```

Pause containers or forcefully kill them

- pause: freezes containers (keeps in memory)
- unpause: resumes paused containers
- kill: sends SIGKILL (force termination)

### Execution & Interaction (exec/run/logs)

Run commands in containers and view logs

**Keywords:** exec, run, logs, interaction, debugging

#### Execute Commands in Running Container

```bash
docker compose exec app npm test
docker compose exec -u root app apt-get update
docker compose exec db psql -U postgres -d mydb
docker compose exec -it app /bin/bash
```

_exec_
```bash
> npm test

PASS __tests__/app.test.js
  App
    ✓ should start successfully (142ms)

Test Suites: 1 passed
Tests: 1 passed
```

Run commands in already running containers

- -i: interactive
- -t: allocate pseudo-terminal
- -u: run as specific user

#### Run One-Off Container

```bash
docker compose run app npm install
docker compose run --rm worker node scripts/migrate.js
docker compose run -e NODE_ENV=test app npm test
```

_exec_
```bash
> npm install
added 150 packages in 45s
```

Run temporary containers to execute commands

- run: starts new container for command
- --rm: automatically remove container after exit
- -e: set environment variables

#### View Container Logs

```bash
docker compose logs
docker compose logs app
docker compose logs -f app
docker compose logs --tail=100 app
docker compose logs --timestamps app
```

_exec_
```bash
app-1     | npm start
app-1     | Server listening on port 3000
app-1     | GET /api/health 200 12ms
app-1     | POST /api/data 201 45ms
```

View output logs from containers

- -f: follow logs in real-time
- --tail: show last N lines
- --timestamps: add timestamps to logs

### Status & Information (ps/config/port)

Check container status and view configurations

**Keywords:** ps, config, port, status, information

#### List Running Containers (docker compose ps)

```bash
docker compose ps
docker compose ps --all
docker compose ps --format json
docker compose ps -a
```

_exec_
```bash
NAME      IMAGE            PORTS                    STATUS
app-1     node:18          0.0.0.0:3000->3000/tcp   Up 2 minutes (healthy)
db-1      postgres:15      0.0.0.0:5432->5432/tcp   Up 3 minutes
cache-1   redis:7          6379/tcp                 Up 2 minutes 45s
```

List all containers defined in compose file

- Shows running containers by default
- -a: include stopped containers
- --format json: output JSON

#### View Merged Configuration

```bash
docker compose config
docker compose config --resolve-image-digests
docker compose config --format json
```

_exec_
```bash
name: my-app
services:
  app:
    image: 'node:18'
    ports:
      - mode: ingress
        target: 3000
        published: "3000"
        protocol: tcp
version: '3.9'
```

Display merged compose configuration from all files

- Shows final resolved configuration
- Useful for debugging variable substitution
- --format json: output as JSON

#### Get Service Port Mappings

```bash
docker compose port app 3000
docker compose port db 5432
docker compose port --index=2 app 3000
```

_exec_
```bash
0.0.0.0:3000
0.0.0.0:5432
```

Show public port for specific container port

- Useful for discovering mapped ports
- --index: for multiple instances

### Build & Push Commands

Build and manage Docker images

**Keywords:** build, push, pull, image management

#### Build Services

```bash
docker compose build
docker compose build app
docker compose build --no-cache
docker compose build --pull
```

_exec_
```bash
[+] Building 12.3s (15/15) FINISHED
 => [app internal] load build definition from Dockerfile
 => [app stage-0 1/5] FROM node:18
 => [app stage-0 2/5] WORKDIR /app
 => [app stage-0 3/5] COPY package*.json ./
 => [app stage-0 4/5] RUN npm ci
 => [app stage-0 5/5] RUN npm run build
 => [app] exporting to image
Successfully tagged my-app-app:latest
```

Build Docker images from specified Dockerfiles

- Builds all services with build context by default
- --no-cache: skip layer cache
- --pull: always update base images

#### Push Images to Registry

```bash
docker compose push
docker compose push app
docker compose push myregistry.azurecr.io/myapp:v1.0
```

_exec_
```bash
Pushing app (myregistry.azurecr.io/myapp:v1.0)...
The push refers to repository [myregistry.azurecr.io/myapp]
abc123: Pushed
def456: Pushed
ghi789: Pushed
v1.0: digest: sha256:1234567890abcdef
```

Push images to Docker registry

- Requires image to be built first
- authenticate to registry before pushing

## Network & Security

### Network Modes & Links

Configure network modes and inter-service communication

**Keywords:** network mode, links, bridge, host, communication

#### Service Discovery via Service Name

```yaml
version: '3.9'
services:
  app:
    image: node:18
    environment:
      DATABASE_URL: postgresql://postgres:5432/mydb
  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: mydb
      POSTGRES_PASSWORD: password
```

_exec_
```bash
$ docker compose up -d
$ docker compose exec app ping postgres
PING postgres (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.123 ms
```

Services communicate automatically using service names as hostnames

- Service names resolve to container IPs
- Works across custom networks
- Default bridge network

#### Legacy Service Links

```yaml
version: '3.9'
services:
  app:
    image: node:18
    links:
      - postgres:database
      - redis:cache
    environment:
      DATABASE_URL: postgresql://database:5432
  postgres:
    image: postgres:15
  redis:
    image: redis:7
```

_exec_
```bash
$ docker compose exec app cat /etc/hosts
127.0.0.1   localhost
172.18.0.2  database
172.18.0.3  cache
```

Create DNS aliases for services (legacy feature)

- Deprecated in favor of custom networks
- Still works for backwards compatibility
- Not recommended for new projects

### User Permissions & Security

Configure user context and security options

**Keywords:** user, permissions, security, uid, gid

#### Run Services as Specific User

```yaml
version: '3.9'
services:
  app:
    image: node:18
    user: "1000:1000"
    working_dir: /app
    volumes:
      - ./src:/app/src
  worker:
    image: node:18
    user: node
    working_dir: /app
```

Run containers as specific user instead of root

- Format: uid:gid or username
- Improves security and file permissions
- Prevents permission issues with mounted volumes

#### Security Options

```yaml
version: '3.9'
services:
  app:
    image: node:18
    security_opt:
      - no-new-privileges:true
  privileged:
    image: ubuntu:latest
    privileged: false
    cap_add:
      - NET_ADMIN
    cap_drop:
      - ALL
```

_exec_
```bash
$ docker compose exec app id
uid=0(root) gid=0(root) groups=0(root)
```

Configure security options and capabilities

- no-new-privileges: prevent privilege escalation
- CAP_ADD: add specific Linux capabilities
- CAP_DROP: remove capabilities

### External Networks & Scaling

Connect to external networks and scale services

**Keywords:** external networks, scaling, replicas, multi-container

#### Scale Services

```bash
docker compose up -d --scale app=3
docker compose up -d --scale worker=5 --scale app=2
```

_exec_
```bash
[+] Running 8/8
 ✔ Container app-1       Started
 ✔ Container app-2       Started
 ✔ Container app-3       Started
 ✔ Container worker-1    Started
 ✔ Container worker-2    Started
 ✔ Container worker-3    Started
 ✔ Container worker-4    Started
 ✔ Container worker-5    Started
```

Run multiple instances of a service for horizontal scaling

- --scale service=count
- Each instance gets unique name (app-1, app-2, etc.)
- Use with load balancer for traffic distribution

#### Connect Multiple Compose Projects

```yaml
version: '3.9'
services:
  app:
    image: node:18
    networks:
      - monolith
      - external-network
networks:
  monolith:
    driver: bridge
  external-network:
    external: true
    name: company-network
```

_exec_
```bash
$ docker compose up -d
[+] Running 1/1
 ✔ Container app-1  Started
$ docker network inspect company-network
[
  {
    "Containers": {
      "abc123": {"Name": "app-1"}
    }
  }
]
```

Connect services to external networks shared across projects

- External networks must be created beforehand
- Multiple compose files can share networks
- Enables multi-service orchestration
