Production: Docker Compose production-ready configuration #330

Closed
opened 2026-03-02 12:32:32 +00:00 by jack · 0 comments
Owner

Problem

Aktuelle Docker-Setup ist für Development optimiert → nicht production-ready.

Fehlende Production-Features:

  • Keine Health-Checks
  • Keine Restart-Policy
  • Keine Resource-Limits
  • Keine Secrets-Management
  • SQLite-DB nicht persistent (Volume fehlt?)

Gewünschte Änderungen

docker-compose.prod.yml erstellen

# docker-compose.prod.yml
version: '3.8'

services:
  backend:
    image: claude-mem-backend:latest
    restart: unless-stopped
    ports:
      - "37777:37777"
    environment:
      NODE_ENV: production
      LOG_LEVEL: info
      DATABASE_URL: ${DATABASE_URL:-sqlite:///data/claude-mem.db}
      API_KEY: ${API_KEY:?API_KEY required}
    volumes:
      - claude-mem-data:/data
      - ./settings.json:/app/settings.json:ro
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:37777/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '1.0'
          memory: 512M
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
  
  worker:
    image: claude-mem-worker:latest
    restart: unless-stopped
    environment:
      NODE_ENV: production
      LOG_LEVEL: info
      DATABASE_URL: ${DATABASE_URL}
      ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?ANTHROPIC_API_KEY required}
    volumes:
      - claude-mem-data:/data
    depends_on:
      backend:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: '4.0'
          memory: 4G
  
  ui:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./packages/ui/dist:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - backend
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
      interval: 30s
  
  # Optional: PostgreSQL statt SQLite
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: claudemem
      POSTGRES_USER: ${POSTGRES_USER:-claudemem}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD required}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U claudemem"]
      interval: 10s

volumes:
  claude-mem-data:
  postgres-data:

Health-Check Endpoint

// packages/backend/src/routes/health.ts
app.get('/health', async (req, res) => {
  try {
    // DB-Check
    await em.getConnection().execute('SELECT 1');
    
    res.json({
      status: 'healthy',
      uptime: process.uptime(),
      database: 'connected',
      version: process.env.npm_package_version
    });
  } catch (e) {
    res.status(503).json({
      status: 'unhealthy',
      error: e.message
    });
  }
});

nginx.conf (Reverse Proxy für UI)

events {
  worker_connections 1024;
}

http {
  include mime.types;
  
  server {
    listen 80;
    server_name _;
    
    # UI (Static Files)
    location / {
      root /usr/share/nginx/html;
      try_files $uri $uri/ /index.html;
    }
    
    # API Proxy
    location /api/ {
      proxy_pass http://backend:37777/api/;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_cache_bypass $http_upgrade;
    }
    
    # SSE Proxy
    location /api/stream {
      proxy_pass http://backend:37777/api/stream;
      proxy_http_version 1.1;
      proxy_set_header Connection '';
      proxy_buffering off;
      proxy_cache off;
      chunked_transfer_encoding off;
    }
  }
}

.env.example (Production)

# .env.prod.example
NODE_ENV=production
DATABASE_URL=postgres://user:pass@postgres:5432/claudemem
API_KEY=generated-secure-key-here
ANTHROPIC_API_KEY=sk-ant-...
LOG_LEVEL=info
BACKEND_PORT=37777

Deployment-Script

#!/bin/bash
# scripts/deploy.sh

set -e

echo "Building images..."
docker compose -f docker-compose.prod.yml build

echo "Pulling latest images..."
docker compose -f docker-compose.prod.yml pull

echo "Starting services..."
docker compose -f docker-compose.prod.yml up -d

echo "Waiting for health checks..."
sleep 10

echo "Checking health..."
curl -f http://localhost:37777/health || exit 1

echo "✅ Deployment successful"

Secrets-Management

Option 1: Docker Secrets (Swarm)

secrets:
  api_key:
    external: true

services:
  backend:
    secrets:
      - api_key

Option 2: .env File (einfacher)

# Nicht committen!
cp .env.prod.example .env.prod
# API-Keys eintragen
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d

Monitoring

Prometheus + Grafana (optional):

services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
  
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"

Acceptance Criteria

  • docker-compose.prod.yml erstellt
  • Health-Check Endpoints
  • Restart-Policy (unless-stopped)
  • Resource-Limits
  • Persistent Volumes
  • nginx Reverse-Proxy
  • Secrets via .env oder Docker Secrets
  • Deploy-Script
  • Dokumentation (README.md)

Priority

High - Wichtig für Production-Deployment.

CI/CD Pipeline und Backup-Strategie werden in separaten Issues behandelt.

## Problem Aktuelle Docker-Setup ist für **Development** optimiert → nicht production-ready. **Fehlende Production-Features:** - Keine Health-Checks - Keine Restart-Policy - Keine Resource-Limits - Keine Secrets-Management - SQLite-DB nicht persistent (Volume fehlt?) ## Gewünschte Änderungen ### docker-compose.prod.yml erstellen ```yaml # docker-compose.prod.yml version: '3.8' services: backend: image: claude-mem-backend:latest restart: unless-stopped ports: - "37777:37777" environment: NODE_ENV: production LOG_LEVEL: info DATABASE_URL: ${DATABASE_URL:-sqlite:///data/claude-mem.db} API_KEY: ${API_KEY:?API_KEY required} volumes: - claude-mem-data:/data - ./settings.json:/app/settings.json:ro healthcheck: test: ["CMD", "curl", "-f", "http://localhost:37777/health"] interval: 30s timeout: 5s retries: 3 start_period: 40s deploy: resources: limits: cpus: '2.0' memory: 2G reservations: cpus: '1.0' memory: 512M logging: driver: json-file options: max-size: "10m" max-file: "3" worker: image: claude-mem-worker:latest restart: unless-stopped environment: NODE_ENV: production LOG_LEVEL: info DATABASE_URL: ${DATABASE_URL} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?ANTHROPIC_API_KEY required} volumes: - claude-mem-data:/data depends_on: backend: condition: service_healthy deploy: resources: limits: cpus: '4.0' memory: 4G ui: image: nginx:alpine restart: unless-stopped ports: - "8080:80" volumes: - ./packages/ui/dist:/usr/share/nginx/html:ro - ./nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - backend healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"] interval: 30s # Optional: PostgreSQL statt SQLite postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: claudemem POSTGRES_USER: ${POSTGRES_USER:-claudemem} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD required} volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U claudemem"] interval: 10s volumes: claude-mem-data: postgres-data: ``` ### Health-Check Endpoint ```typescript // packages/backend/src/routes/health.ts app.get('/health', async (req, res) => { try { // DB-Check await em.getConnection().execute('SELECT 1'); res.json({ status: 'healthy', uptime: process.uptime(), database: 'connected', version: process.env.npm_package_version }); } catch (e) { res.status(503).json({ status: 'unhealthy', error: e.message }); } }); ``` ### nginx.conf (Reverse Proxy für UI) ```nginx events { worker_connections 1024; } http { include mime.types; server { listen 80; server_name _; # UI (Static Files) location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } # API Proxy location /api/ { proxy_pass http://backend:37777/api/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } # SSE Proxy location /api/stream { proxy_pass http://backend:37777/api/stream; proxy_http_version 1.1; proxy_set_header Connection ''; proxy_buffering off; proxy_cache off; chunked_transfer_encoding off; } } } ``` ### .env.example (Production) ```bash # .env.prod.example NODE_ENV=production DATABASE_URL=postgres://user:pass@postgres:5432/claudemem API_KEY=generated-secure-key-here ANTHROPIC_API_KEY=sk-ant-... LOG_LEVEL=info BACKEND_PORT=37777 ``` ### Deployment-Script ```bash #!/bin/bash # scripts/deploy.sh set -e echo "Building images..." docker compose -f docker-compose.prod.yml build echo "Pulling latest images..." docker compose -f docker-compose.prod.yml pull echo "Starting services..." docker compose -f docker-compose.prod.yml up -d echo "Waiting for health checks..." sleep 10 echo "Checking health..." curl -f http://localhost:37777/health || exit 1 echo "✅ Deployment successful" ``` ## Secrets-Management **Option 1: Docker Secrets (Swarm)** ```yaml secrets: api_key: external: true services: backend: secrets: - api_key ``` **Option 2: .env File (einfacher)** ```bash # Nicht committen! cp .env.prod.example .env.prod # API-Keys eintragen docker compose --env-file .env.prod -f docker-compose.prod.yml up -d ``` ## Monitoring **Prometheus + Grafana (optional):** ```yaml services: prometheus: image: prom/prometheus volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml grafana: image: grafana/grafana ports: - "3000:3000" ``` ## Acceptance Criteria - [x] docker-compose.prod.yml erstellt - [x] Health-Check Endpoints - [x] Restart-Policy (unless-stopped) - [x] Resource-Limits - [x] Persistent Volumes - [x] nginx Reverse-Proxy - [x] Secrets via .env oder Docker Secrets - [x] Deploy-Script - [x] Dokumentation (README.md) ## Priority **High** - Wichtig für Production-Deployment. ## Related CI/CD Pipeline und Backup-Strategie werden in separaten Issues behandelt.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
customable/claude-mem#330
No description provided.