# ============================================================ # docker-compose.ionos.yml # Stack production pour Ionos VPS # Usage : docker compose -f docker-compose.ionos.yml up -d # ============================================================ version: '3.8' services: # ── PostgreSQL ────────────────────────────────────────────── postgres: image: postgres:16-alpine container_name: translate-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: - internal healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-translate} -d ${POSTGRES_DB:-translate_db}"] interval: 10s timeout: 5s retries: 5 start_period: 20s # ── Redis ─────────────────────────────────────────────────── redis: image: redis:7-alpine container_name: translate-redis restart: unless-stopped command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --save 60 1 volumes: - redis_data:/data networks: - internal healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # ── Backend FastAPI ───────────────────────────────────────── backend: build: context: . dockerfile: docker/backend/Dockerfile target: production container_name: translate-backend restart: unless-stopped 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 ENV: production LOG_FORMAT: json ENABLE_HSTS: "true" volumes: - uploads_data:/app/uploads - outputs_data:/app/outputs - ./data:/app/data # users.json, provider_settings.json persistent networks: - internal 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: 30s # ── Frontend Next.js ──────────────────────────────────────── frontend: build: context: . dockerfile: docker/frontend/Dockerfile target: production args: NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL} container_name: translate-frontend restart: unless-stopped environment: NODE_ENV: production PORT: 3000 networks: - internal depends_on: - backend healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000"] interval: 30s timeout: 10s retries: 3 # ── Nginx + Certbot SSL ───────────────────────────────────── nginx: image: nginx:alpine container_name: translate-nginx restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./docker/nginx/conf.d:/etc/nginx/conf.d:ro - certbot_www:/var/www/certbot:ro - certbot_certs:/etc/letsencrypt:ro networks: - internal depends_on: - backend - frontend healthcheck: test: ["CMD", "nginx", "-t"] interval: 60s timeout: 10s retries: 3 # ── Certbot (Let's Encrypt) ───────────────────────────────── certbot: image: certbot/certbot:latest container_name: translate-certbot volumes: - certbot_www:/var/www/certbot - certbot_certs:/etc/letsencrypt # Renouvellement automatique toutes les 12h entrypoint: > /bin/sh -c "trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot --quiet; sleep 12h & wait $${!}; done" networks: - internal volumes: postgres_data: redis_data: uploads_data: outputs_data: certbot_www: certbot_certs: networks: internal: driver: bridge