version: "3.9" # ══════════════════════════════════════════════════════ # تبيان الطبي — Production Docker Compose # Oracle Cloud Always Free ARM VM (4 CPU / 24 GB RAM) # # First-time HTTPS setup: # bash nginx/init-letsencrypt.sh # # Deploy / update: # docker compose -f docker-compose.prod.yml pull # docker compose -f docker-compose.prod.yml up -d --build # ══════════════════════════════════════════════════════ services: # ── Redis ──────────────────────────────────────────────────────────────────── redis: image: redis:7.2-alpine restart: unless-stopped command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru --save "" expose: - "6379" volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 5 networks: - tebyan_net # ── Backend (FastAPI) ──────────────────────────────────────────────────────── backend: build: context: ./backend dockerfile: Dockerfile restart: unless-stopped env_file: .env environment: ENVIRONMENT: production PORT: "8000" REDIS_URL: redis://redis:6379/0 AUDIT_LOG_DIR: /app/logs/audit HF_HOME: /app/model_cache volumes: - model_cache:/app/model_cache - audit_logs:/app/logs expose: - "8000" depends_on: redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 120s deploy: resources: limits: memory: 6G cpus: "3.0" reservations: memory: 2G networks: - tebyan_net # ── Frontend (Next.js) ─────────────────────────────────────────────────────── frontend: build: context: ./frontend dockerfile: Dockerfile args: NEXT_PUBLIC_BACKEND_URL: ${NEXT_PUBLIC_BACKEND_URL:-/} NEXT_PUBLIC_SUPABASE_URL: ${SUPABASE_URL} NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_KEY} restart: unless-stopped environment: NODE_ENV: production NEXT_PUBLIC_BACKEND_URL: ${NEXT_PUBLIC_BACKEND_URL:-/} NEXT_PUBLIC_SUPABASE_URL: ${SUPABASE_URL} NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_KEY} expose: - "3000" depends_on: backend: condition: service_healthy healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health-check || exit 0"] interval: 30s timeout: 10s retries: 3 start_period: 30s deploy: resources: limits: memory: 1G cpus: "1.0" networks: - tebyan_net # ── Nginx reverse proxy + SSL ──────────────────────────────────────────────── nginx: image: nginx:1.27-alpine restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/conf.d:/etc/nginx/conf.d:ro - certbot_www:/var/www/certbot:ro - certbot_certs:/etc/letsencrypt:ro depends_on: - frontend - backend deploy: resources: limits: memory: 128M cpus: "0.25" networks: - tebyan_net # ── Certbot (Let's Encrypt) ────────────────────────────────────────────────── certbot: image: certbot/certbot:latest volumes: - certbot_www:/var/www/certbot - certbot_certs:/etc/letsencrypt # Renews automatically — runs every 12h, renews when <30 days left entrypoint: > /bin/sh -c "trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot --quiet; sleep 12h & wait $${!}; done" volumes: model_cache: audit_logs: redis_data: certbot_www: certbot_certs: networks: tebyan_net: driver: bridge