# ============================================ # Wordly.art - Production Docker Compose # ============================================ # Architecture: Nginx Proxy Manager (NPM) gere le SSL et reverse proxy # NPM pointe vers les services internes sur ce réseau Docker # # Usage: # docker compose up -d # # Puis configurer NPM pour pointer vers: # wordly.art -> frontend:3000 (et backend:8000 pour /api/ et /translate) # ============================================ services: # =========================================== # PostgreSQL Database # =========================================== postgres: image: postgres:16-alpine container_name: wordly-postgres restart: unless-stopped environment: - POSTGRES_USER=${POSTGRES_USER:-translate} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=${POSTGRES_DB:-translate_db} - PGDATA=/var/lib/postgresql/data/pgdata volumes: - postgres_data:/var/lib/postgresql/data networks: - wordly-network healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-translate} -d ${POSTGRES_DB:-translate_db}"] interval: 10s timeout: 5s retries: 5 start_period: 10s deploy: resources: limits: memory: 512M reservations: memory: 128M # =========================================== # Redis (Caching & Sessions) # =========================================== redis: image: redis:7-alpine container_name: wordly-redis restart: unless-stopped command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru volumes: - redis_data:/data networks: - wordly-network healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 start_period: 5s # =========================================== # Backend API (FastAPI) # =========================================== backend: build: context: . dockerfile: docker/backend/Dockerfile target: production container_name: wordly-backend restart: unless-stopped ports: - "8001:8000" env_file: - .env environment: - DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER:-translate}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-translate_db} - REDIS_URL=redis://redis:6379/0 - TRANSLATION_SERVICE=${TRANSLATION_SERVICE:-ollama} - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://ollama:11434} - OLLAMA_MODEL=${OLLAMA_MODEL:-llama3} - DEEPL_API_KEY=${DEEPL_API_KEY:-} - OPENAI_API_KEY=${OPENAI_API_KEY:-} - OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-} - DEEPSEEK_ENABLED=${DEEPSEEK_ENABLED:-false} - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY:-} - DEEPSEEK_MODEL=${DEEPSEEK_MODEL:-deepseek-chat} - DEEPSEEK_BASE_URL=${DEEPSEEK_BASE_URL:-https://api.deepseek.com/v1} - MINIMAX_ENABLED=${MINIMAX_ENABLED:-false} - MINIMAX_API_KEY=${MINIMAX_API_KEY:-} - MINIMAX_MODEL=${MINIMAX_MODEL:-MiniMax-M1} - MINIMAX_BASE_URL=${MINIMAX_BASE_URL:-https://api.minimax.chat/v1} - MAX_FILE_SIZE_MB=${MAX_FILE_SIZE_MB:-50} - RATE_LIMIT_REQUESTS_PER_MINUTE=${RATE_LIMIT_REQUESTS_PER_MINUTE:-60} - RATE_LIMIT_TRANSLATIONS_PER_MINUTE=${RATE_LIMIT_TRANSLATIONS_PER_MINUTE:-10} - ADMIN_USERNAME=${ADMIN_USERNAME} - ADMIN_PASSWORD=${ADMIN_PASSWORD} - JWT_SECRET_KEY=${JWT_SECRET_KEY} - ADMIN_TOKEN_SECRET=${ADMIN_TOKEN_SECRET} - CORS_ORIGINS=${CORS_ORIGINS:-https://wordly.art} - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY:-} - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET:-} - STRIPE_STARTER_PRICE_ID=${STRIPE_STARTER_PRICE_ID:-} - STRIPE_PRO_PRICE_ID=${STRIPE_PRO_PRICE_ID:-} - STRIPE_BUSINESS_PRICE_ID=${STRIPE_BUSINESS_PRICE_ID:-} - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID:-} volumes: - uploads_data:/app/uploads - outputs_data:/app/outputs - logs_data:/app/logs networks: - wordly-network depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 15s deploy: resources: limits: memory: 2G reservations: memory: 512M # =========================================== # Frontend (Next.js) # =========================================== frontend: build: context: . dockerfile: docker/frontend/Dockerfile target: production args: NEXT_PUBLIC_API_URL: "" container_name: wordly-frontend restart: unless-stopped ports: - "3000:3000" env_file: - .env environment: - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-https://wordly.art} - NEXT_PUBLIC_GOOGLE_CLIENT_ID=${NEXT_PUBLIC_GOOGLE_CLIENT_ID:-} networks: - wordly-network depends_on: backend: condition: service_healthy deploy: resources: limits: memory: 512M reservations: memory: 128M # =========================================== # Ollama (Optional - Local LLM) # =========================================== ollama: image: ollama/ollama:latest container_name: wordly-ollama restart: unless-stopped volumes: - ollama_data:/root/.ollama networks: - wordly-network deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] profiles: - with-ollama # =========================================== # Prometheus - Metrics Collection # =========================================== prometheus: image: prom/prometheus:v2.52.0 container_name: wordly-prometheus restart: unless-stopped volumes: - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./docker/prometheus/alerts.yml:/etc/prometheus/alerts.yml:ro - prometheus_data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--storage.tsdb.retention.time=30d' - '--storage.tsdb.retention.size=5GB' - '--web.enable-lifecycle' networks: - wordly-network healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:9090/-/healthy"] interval: 30s timeout: 5s retries: 3 # =========================================== # Grafana - Dashboards & Visualization # =========================================== grafana: image: grafana/grafana:11.0.0 container_name: wordly-grafana restart: unless-stopped environment: - GF_SECURITY_ADMIN_USER=${GRAFANA_USER:-admin} - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-WordlyGrafana2026!} - GF_USERS_ALLOW_SIGN_UP=false - GF_SERVER_ROOT_URL=https://monitoring.wordly.art - GF_INSTALL_PLUGINS=grafana-clock-panel volumes: - grafana_data:/var/lib/grafana - ./docker/grafana/provisioning:/etc/grafana/provisioning:ro - ./docker/grafana/dashboards:/var/lib/grafana/dashboards:ro ports: - "3001:3000" networks: - wordly-network depends_on: prometheus: condition: service_healthy healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"] interval: 30s timeout: 5s retries: 3 # =========================================== # Node Exporter - System Metrics # =========================================== node-exporter: image: prom/node-exporter:v1.8.0 container_name: wordly-node-exporter restart: unless-stopped pid: host volumes: - /proc:/host/proc:ro - /sys:/host/sys:ro - /:/rootfs:ro command: - '--path.procfs=/host/proc' - '--path.sysfs=/host/sys' - '--path.rootfs=/rootfs' - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|etc|var/lib/docker)($$|/)' networks: - wordly-network # =========================================== # cAdvisor - Container Metrics # =========================================== cadvisor: image: gcr.io/cadvisor/cadvisor:v0.49.1 container_name: wordly-cadvisor restart: unless-stopped volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro - /dev/disk:/dev/disk:ro privileged: true devices: - /dev/kmsg networks: - wordly-network # =========================================== # Networks # =========================================== networks: wordly-network: driver: bridge name: wordly-network # =========================================== # Volumes # =========================================== volumes: postgres_data: driver: local uploads_data: driver: local outputs_data: driver: local logs_data: driver: local redis_data: driver: local ollama_data: driver: local prometheus_data: driver: local grafana_data: driver: local