version: "3.8" services: postgres: image: postgres:16-alpine environment: POSTGRES_DB: tagcast POSTGRES_USER: postgres POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres} # Only expose to host in development — comment out in production # ports: # - "127.0.0.1:5432:5432" volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 deploy: resources: limits: memory: 1G cpus: '1.0' logging: driver: json-file options: max-size: "10m" max-file: "3" redis: image: redis:7-alpine command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru # Only expose to host in development — comment out in production # ports: # - "127.0.0.1:6379:6379" volumes: - redisdata:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 5 deploy: resources: limits: memory: 512M cpus: '0.5' logging: driver: json-file options: max-size: "10m" max-file: "3" backend: build: context: ./backend dockerfile: Dockerfile ports: - "8000:8000" environment: DATABASE_URL: postgresql://postgres:${DB_PASSWORD:-postgres}@postgres:5432/tagcast REDIS_URL: redis://redis:6379/0 SECRET_KEY: ${SECRET_KEY:?Set SECRET_KEY in .env (min 32 chars)} ENV: ${ENV:-production} CORS_ORIGINS: '["${FRONTEND_URL:-http://localhost}"]' UPLOAD_DIR: /app/uploads volumes: - uploads:/app/uploads depends_on: postgres: condition: service_healthy redis: condition: service_healthy deploy: resources: limits: memory: 2G cpus: '2.0' logging: driver: json-file options: max-size: "20m" max-file: "5" nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/certs:/etc/nginx/certs:ro - ./assets:/usr/share/nginx/html/assets:ro - ./pages:/usr/share/nginx/html/pages:ro - ./index.html:/usr/share/nginx/html/index.html:ro depends_on: - backend deploy: resources: limits: memory: 256M cpus: '0.5' logging: driver: json-file options: max-size: "10m" max-file: "3" volumes: pgdata: redisdata: uploads: