# 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`

```yaml
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`

```yaml
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

```bash
# 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

```bash
# Stop all services
docker compose down

# Stop and remove volumes
docker compose down -v

# Stop specific service
docker compose stop api
```

### Building Services

```bash
# 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

```bash
# 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

```bash
# List running services
docker compose ps

# Check service health
docker compose ps

# View service details
docker inspect <service-name>
```

## 🔍 Service Management

### Restart Services

```bash
# Restart all
docker compose restart

# Restart specific service
docker compose restart api

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

### Scale Services

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

# Check scaled services
docker compose ps
```

### Exec into Containers

```bash
# 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:

```yaml
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**:

```javascript
// 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**:

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

### Custom Networks

```yaml
networks:
  frontend:
  backend:
  database:

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

## 💾 Volume Management

### Named Volumes

```yaml
volumes:
  postgres_data:      # Managed by Docker
  redis_data:         # Persists between restarts
```

```bash
# 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)

```yaml
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

```bash
# 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):

```bash
# 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

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

### Override with Shell

```bash
# 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

```yaml
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

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

### API Health Check

```yaml
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

```yaml
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

```bash
# 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

```bash
# 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**:

```bash
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**:

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

### Database Connection Issues

**Problem**: API can't connect to postgres

**Solutions**:

```bash
# 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**:

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

### Stale Data in Volumes

**Problem**: Old data persisting

**Solution**:

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

## 📊 Monitoring Services

### Resource Usage

```bash
# CPU and memory usage
docker stats

# Specific services
docker stats api web
```

### Container Inspection

```bash
# 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

- **Docker Setup**: [Dockerfile configuration](./docker.md)
- **Production**: [Production deployment](./production.md)
- **Environment**: [Environment variables](./environment-setup.md)
- **Monitoring**: [Monitoring and logging](./monitoring.md)

---

*Ready for production? Continue with [Production Deployment](./production.md) →*
