Isaac.

Docker Compose: Multi-Container Applications

Manage multiple containers with Docker Compose for local development.

By EMEPublished: February 20, 2025
dockerdocker-composecontainersorchestrationdevelopment

A Simple Analogy

Docker Compose is like a restaurant setup script. Instead of hiring employees one by one and explaining their roles, you give one script that sets up the kitchen, front desk, and delivery—all configured to work together automatically.


What Is Docker Compose?

Docker Compose is a tool for defining and running multi-container applications. It uses a YAML file to configure all services, networks, and volumes, then starts them with one command.


Why Use Docker Compose?

  • Local development: Replicate production locally
  • Simple startup: One command starts entire app
  • Service linking: Containers communicate automatically
  • Volume management: Persist data across runs
  • Environment variables: Configure per service

Basic Structure

version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://db:5432/myapp
    depends_on:
      - db
  
  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_PASSWORD=secret
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

Common Commands

# Start services
docker-compose up

# Start in background
docker-compose up -d

# View running services
docker-compose ps

# View logs
docker-compose logs -f web

# Stop services
docker-compose down

# Rebuild images
docker-compose build

Practical Example

version: '3.8'

services:
  app:
    build: .
    container_name: myapp
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache
    volumes:
      - .:/app
      - /app/node_modules
    networks:
      - app-network

  db:
    image: postgres:15
    container_name: myapp_db
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    ports:
      - "5432:5432"
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - app-network

  cache:
    image: redis:7-alpine
    container_name: myapp_cache
    ports:
      - "6379:6379"
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  db_data:

Usage:

docker-compose up -d
# App available at http://localhost:3000

Service Communication

services:
  api:
    environment:
      - DB_HOST=postgres  # Service name, not IP
      - DB_PORT=5432

  postgres:
    # Services on same network can access via service name

In code:

const dbUrl = "postgresql://user:pass@postgres:5432/myapp";
// postgres is the service name from docker-compose.yml

Best Practices

  1. Use services names: Don't hardcode IPs
  2. Define networks: Control service communication
  3. Version control: Commit docker-compose.yml
  4. Environment files: Use .env for sensitive data
  5. Health checks: Ensure services are ready

Related Concepts to Explore

  • Kubernetes (production orchestration)
  • Docker networking
  • Volume management and persistence
  • Environment files and secrets

Summary

Docker Compose simplifies local development with multiple services. Define your entire stack once and start it reliably on any machine with one command.