version: '3.8' services: # Traefik reverse proxy traefik: image: traefik:v3.0 container_name: manacore-traefik restart: unless-stopped command: - "--api.dashboard=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true" - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" # Rate limiting - "--api.insecure=false" ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - traefik-letsencrypt:/letsencrypt networks: - manacore-network # PostgreSQL database postgres: image: postgres:16-alpine container_name: manacore-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-manacore} POSTGRES_USER: ${POSTGRES_USER:-manacore} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_INITDB_ARGS: "--encoding=UTF8 --auth=scram-sha-256" volumes: - postgres-data:/var/lib/postgresql/data - ./services/mana-core-auth/postgres/init:/docker-entrypoint-initdb.d:ro ports: - "5432:5432" networks: - manacore-network healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-manacore}"] interval: 10s timeout: 5s retries: 5 command: - "postgres" - "-c" - "max_connections=200" - "-c" - "shared_buffers=256MB" - "-c" - "effective_cache_size=1GB" - "-c" - "password_encryption=scram-sha-256" # PgBouncer connection pooler pgbouncer: image: pgbouncer/pgbouncer:latest container_name: manacore-pgbouncer restart: unless-stopped environment: DATABASES_HOST: postgres DATABASES_PORT: 5432 DATABASES_USER: ${POSTGRES_USER:-manacore} DATABASES_PASSWORD: ${POSTGRES_PASSWORD} DATABASES_DBNAME: ${POSTGRES_DB:-manacore} PGBOUNCER_POOL_MODE: transaction PGBOUNCER_MAX_CLIENT_CONN: 1000 PGBOUNCER_DEFAULT_POOL_SIZE: 25 depends_on: postgres: condition: service_healthy networks: - manacore-network # Redis cache redis: image: redis:7-alpine container_name: manacore-redis restart: unless-stopped command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru volumes: - redis-data:/data ports: - "6379:6379" networks: - manacore-network healthcheck: test: ["CMD", "redis-cli", "--raw", "incr", "ping"] interval: 10s timeout: 5s retries: 5 # Mana Core Auth Service mana-core-auth: build: context: . dockerfile: ./services/mana-core-auth/Dockerfile container_name: manacore-auth restart: unless-stopped environment: NODE_ENV: production PORT: 3001 DATABASE_URL: postgresql://${POSTGRES_USER:-manacore}:${POSTGRES_PASSWORD}@pgbouncer:6432/${POSTGRES_DB:-manacore} REDIS_HOST: redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD} # JWT keys managed automatically by Better Auth (EdDSA) - stored in auth.jwks table JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m} JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d} JWT_ISSUER: ${JWT_ISSUER:-manacore} JWT_AUDIENCE: ${JWT_AUDIENCE:-manacore} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY} STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET} STRIPE_PUBLISHABLE_KEY: ${STRIPE_PUBLISHABLE_KEY} CORS_ORIGINS: ${CORS_ORIGINS} CREDITS_SIGNUP_BONUS: ${CREDITS_SIGNUP_BONUS:-150} CREDITS_DAILY_FREE: ${CREDITS_DAILY_FREE:-5} depends_on: postgres: condition: service_healthy redis: condition: service_healthy networks: - manacore-network labels: - "traefik.enable=true" - "traefik.http.routers.manacore-auth.rule=Host(`${AUTH_DOMAIN}`)" - "traefik.http.routers.manacore-auth.entrypoints=websecure" - "traefik.http.routers.manacore-auth.tls.certresolver=letsencrypt" - "traefik.http.services.manacore-auth.loadbalancer.server.port=3001" # Rate limiting - "traefik.http.middlewares.auth-ratelimit.ratelimit.average=100" - "traefik.http.middlewares.auth-ratelimit.ratelimit.burst=50" - "traefik.http.routers.manacore-auth.middlewares=auth-ratelimit" # Prometheus (metrics) prometheus: image: prom/prometheus:latest container_name: manacore-prometheus restart: unless-stopped volumes: - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus-data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--web.console.libraries=/usr/share/prometheus/console_libraries' - '--web.console.templates=/usr/share/prometheus/consoles' ports: - "9090:9090" networks: - manacore-network # Grafana (dashboards) grafana: image: grafana/grafana:latest container_name: manacore-grafana restart: unless-stopped environment: GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD} GF_USERS_ALLOW_SIGN_UP: false volumes: - grafana-data:/var/lib/grafana - ./docker/grafana/provisioning:/etc/grafana/provisioning:ro ports: - "3000:3000" depends_on: - prometheus networks: - manacore-network networks: manacore-network: driver: bridge volumes: postgres-data: redis-data: traefik-letsencrypt: prometheus-data: grafana-data: