Docs /deployment/docker-compose

Docker Compose

Multi-service orchestration for development and production

📋 Overview

Docker Compose orchestrates all Izri services, handling networking, volumes, and service dependencies. We maintain separate configurations for development and production environments.

🐳 Compose Files

File Structure

project-root/
├── docker-compose.yml          # Production configuration
├── docker-compose.dev.yml      # Development configuration (with hot reload)
└── docker/
    ├── web/Dockerfile
    ├── api/Dockerfile
    └── ai-service/Dockerfile

📦 Production Configuration

File: docker-compose.yml

services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: izri
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    ports:
      - "5433:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6380:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  api:
    build:
      context: .
      dockerfile: docker/api/Dockerfile
    ports:
      - "4000:4000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/izri
      - REDIS_URL=redis://redis:6379
      - LOG_LEVEL=info
      - NODE_ENV=production
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  web:
    build:
      context: .
      dockerfile: docker/web/Dockerfile
    ports:
      - "3000:3000"
    environment:
      - VITE_API_URL=http://api:4000
      - NODE_ENV=production
    depends_on:
      - api

  ai-service:
    build:
      context: .
      dockerfile: docker/ai-service/Dockerfile
    ports:
      - "8000:8000"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}

volumes:
  postgres_data:

🔧 Development Configuration

File: docker-compose.dev.yml

services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: izri_dev
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    ports:
      - "5433:5432"
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6380:6379"
    volumes:
      - redis_dev_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  # API with hot reload
  api:
    build:
      context: .
      dockerfile: docker/api/Dockerfile.dev
    ports:
      - "4000:4000"
    volumes:
      - ./apps/api:/app/apps/api
      - ./packages:/app/packages
      - /app/node_modules
    environment:
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/izri_dev
      - REDIS_URL=redis://redis:6379
      - LOG_LEVEL=debug
      - NODE_ENV=development
    depends_on:
      postgres:
        condition: service_healthy

  # Web with hot reload
  web:
    build:
      context: .
      dockerfile: docker/web/Dockerfile.dev
    ports:
      - "5173:5173"
    volumes:
      - ./apps/web:/app/apps/web
      - ./packages:/app/packages
      - /app/node_modules
    environment:
      - VITE_API_URL=http://localhost:4000
      - NODE_ENV=development
    depends_on:
      - api

  ai-service:
    build:
      context: .
      dockerfile: docker/ai-service/Dockerfile
    ports:
      - "8000:8000"
    volumes:
      - ./packages/ai-service:/app/packages/ai-service
      - /app/packages/ai-service/__pycache__
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
      - LOG_LEVEL=debug

volumes:
  postgres_dev_data:
  redis_dev_data:

🚀 Common Commands

Starting Services

# Start all services (production)
docker compose up

# Start in detached mode (background)
docker compose up -d

# Start specific services
docker compose up postgres redis

# Development mode (with hot reload)
docker compose -f docker-compose.dev.yml up

Stopping Services

# Stop all services
docker compose down

# Stop and remove volumes
docker compose down -v

# Stop specific service
docker compose stop api

Building Services

# Build all services
docker compose build

# Build specific service
docker compose build web

# Build with no cache
docker compose build --no-cache

# Parallel builds
docker compose build --parallel

Viewing Logs

# All services
docker compose logs

# Follow logs (live)
docker compose logs -f

# Specific service
docker compose logs api

# Last 100 lines
docker compose logs --tail=100

# Since timestamp
docker compose logs --since 2024-01-01T00:00:00

Service Status

# List running services
docker compose ps

# Check service health
docker compose ps

# View service details
docker inspect <service-name>

🔍 Service Management

Restart Services

# Restart all
docker compose restart

# Restart specific service
docker compose restart api

# Restart with rebuild
docker compose up -d --build api

Scale Services

# Run multiple instances
docker compose up -d --scale api=3

# Check scaled services
docker compose ps

Exec into Containers

# API container
docker compose exec api sh

# Postgres container
docker compose exec postgres psql -U postgres -d izri

# Redis container
docker compose exec redis redis-cli

🌐 Networking

Default Network

Docker Compose creates a default network where all services can communicate:

services:
  api:
    # Can access postgres at: postgres:5432
    # Can access redis at: redis:6379
  
  postgres:
    # Accessible from other services as 'postgres'

Service Discovery

Internal communication:

// API connects to postgres
const DATABASE_URL = 'postgresql://postgres:password@postgres:5432/izri'

// API connects to redis
const REDIS_URL = 'redis://redis:6379'

External access:

# From host machine
psql -h localhost -p 5433 -U postgres -d izri
redis-cli -h localhost -p 6380

Custom Networks

networks:
  frontend:
  backend:
  database:

services:
  web:
    networks:
      - frontend
  
  api:
    networks:
      - frontend
      - backend
  
  postgres:
    networks:
      - backend

💾 Volume Management

Named Volumes

volumes:
  postgres_data:      # Managed by Docker
  redis_data:         # Persists between restarts
# List volumes
docker volume ls

# Inspect volume
docker volume inspect izri_postgres_data

# Remove volume
docker volume rm izri_postgres_data

# Remove all unused volumes
docker volume prune

Bind Mounts (Development)

services:
  api:
    volumes:
      - ./apps/api:/app/apps/api          # Source code
      - /app/node_modules                 # Anonymous volume (don't override)

How it works:

  • Host directory ./apps/api mounted into container
  • Changes on host immediately reflected in container
  • Enables hot reload for development

Volume Backup

# Backup postgres volume
docker run --rm \
  -v izri_postgres_data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/postgres-backup.tar.gz /data

# Restore postgres volume
docker run --rm \
  -v izri_postgres_data:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/postgres-backup.tar.gz -C /

🔐 Environment Variables

Using .env File

Create .env file (root directory):

# Database
POSTGRES_USER=postgres
POSTGRES_PASSWORD=secure_password_here
POSTGRES_DB=izri

# API Keys
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GITHUB_CLIENT_ID=github_client_id
GITHUB_CLIENT_SECRET=github_secret

# JWT
JWT_SECRET=your_secure_jwt_secret_here

# Environment
NODE_ENV=production
LOG_LEVEL=info

Load Environment Variables

services:
  api:
    env_file:
      - .env
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}

Override with Shell

# Set variable for this command only
OPENAI_API_KEY=sk-... docker compose up

# Export for all commands
export OPENAI_API_KEY=sk-...
docker compose up

🏥 Health Checks

Postgres Health Check

postgres:
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U postgres"]
    interval: 10s      # Check every 10 seconds
    timeout: 5s        # Fail if takes >5s
    retries: 5         # Try 5 times before marking unhealthy

Redis Health Check

redis:
  healthcheck:
    test: ["CMD", "redis-cli", "ping"]
    interval: 10s
    timeout: 5s
    retries: 5

API Health Check

api:
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
    interval: 30s
    timeout: 10s
    retries: 3
    start_period: 40s  # Don't check for first 40s

Using Health Checks in depends_on

services:
  api:
    depends_on:
      postgres:
        condition: service_healthy  # Wait for postgres to be healthy
      redis:
        condition: service_healthy
  
  web:
    depends_on:
      api:
        condition: service_started  # Just wait for api to start

🔧 Development Workflow

Daily Development

# 1. Start services
pnpm docker:up
# or: docker compose -f docker-compose.dev.yml up -d

# 2. Check status
docker compose ps

# 3. View logs
pnpm docker:logs
# or: docker compose logs -f

# 4. Run migrations
pnpm db:migrate

# 5. Stop services
pnpm docker:down
# or: docker compose down

Rebuilding After Changes

# Rebuild specific service
docker compose up -d --build api

# Rebuild all
docker compose up -d --build

# Force complete rebuild
docker compose down
docker compose build --no-cache
docker compose up -d

🐛 Troubleshooting

Service Won't Start

Check logs:

docker compose logs <service-name>

Common issues:

  • Port already in use: Change port mapping
  • Health check failing: Check service logs
  • Volume permission issues: Check file ownership

Port Conflicts

Problem: Port 5433 already in use

Solution:

postgres:
  ports:
    - "5434:5432"  # Change external port

Database Connection Issues

Problem: API can't connect to postgres

Solutions:

# 1. Check postgres is healthy
docker compose ps

# 2. Check network
docker compose exec api ping postgres

# 3. Verify connection string
docker compose exec api env | grep DATABASE_URL

Volume Permission Errors

Problem: Permission denied on mounted volumes

Solution:

# In Dockerfile, match host user ID
RUN adduser --uid 1000 --disabled-password appuser
USER appuser

Stale Data in Volumes

Problem: Old data persisting

Solution:

# Remove volumes and recreate
docker compose down -v
docker compose up -d

📊 Monitoring Services

Resource Usage

# CPU and memory usage
docker stats

# Specific services
docker stats api web

Container Inspection

# Detailed container info
docker compose exec api env

# Network details
docker network inspect izri_default

# Volume mount points
docker compose exec api df -h

🔗 Related Documentation


Ready for production? Continue with Production Deployment

Reading this with an agent? /docs/deployment/docker-compose.md serves the raw markdown.

All docs