Spaces:
No application file
No application file
| # ============================================================== | |
| # OPTIM AI BRE Engine — Production Docker Compose | |
| # Single command deployment: docker compose up -d | |
| # | |
| # Architecture: | |
| # nginx (port 80/443) → frontend (3000) + backend (8080) | |
| # backend → postgres (5432) + redis (6379) | |
| # | |
| # All services run inside Docker. Nothing needs to be installed | |
| # on the host except Docker Desktop / Docker Engine. | |
| # ============================================================== | |
| name: optim-ai-bre | |
| # ============================================================== | |
| # NETWORKS | |
| # ============================================================== | |
| networks: | |
| bre-public: | |
| driver: bridge | |
| bre-internal: | |
| driver: bridge | |
| internal: true | |
| # ============================================================== | |
| # PERSISTENT VOLUMES | |
| # ============================================================== | |
| volumes: | |
| postgres-data: | |
| driver: local | |
| name: bre_postgres_data | |
| redis-data: | |
| driver: local | |
| name: bre_redis_data | |
| backend-logs: | |
| driver: local | |
| name: bre_backend_logs | |
| backend-reports: | |
| driver: local | |
| name: bre_backend_reports | |
| nginx-logs: | |
| driver: local | |
| name: bre_nginx_logs | |
| db-backups: | |
| driver: local | |
| name: bre_db_backups | |
| # ============================================================== | |
| # SERVICES | |
| # ============================================================== | |
| services: | |
| # ------------------------------------------------------------ | |
| # NGINX — Reverse Proxy & SSL Termination | |
| # Entry point for all traffic on port 80 (and 443 for HTTPS) | |
| # Routes: /api/* → backend | /* → frontend | |
| # ------------------------------------------------------------ | |
| bre-nginx: | |
| image: nginx:${NGINX_VERSION:-1.25-alpine} | |
| container_name: bre-nginx | |
| restart: unless-stopped | |
| ports: | |
| - "80:80" | |
| - "443:443" | |
| volumes: | |
| - ./docker/nginx.conf:/etc/nginx/nginx.conf:ro | |
| - nginx-logs:/var/log/nginx | |
| # SSL certificates (optional — uncomment when you have certs): | |
| # - ./docker/ssl:/etc/nginx/ssl:ro | |
| networks: | |
| - bre-public | |
| - bre-internal | |
| depends_on: | |
| bre-backend: | |
| condition: service_healthy | |
| bre-frontend: | |
| condition: service_healthy | |
| healthcheck: | |
| test: ["CMD", "wget", "-qO-", "http://localhost/nginx-health"] | |
| interval: 30s | |
| timeout: 10s | |
| retries: 3 | |
| start_period: 15s | |
| logging: | |
| driver: json-file | |
| options: | |
| max-size: "10m" | |
| max-file: "5" | |
| # ------------------------------------------------------------ | |
| # BACKEND — .NET 8 BRE Engine API | |
| # Internal only — not exposed to host, nginx proxies to it | |
| # ------------------------------------------------------------ | |
| bre-backend: | |
| build: | |
| context: ./src/backend | |
| dockerfile: ../../docker/Dockerfile.backend | |
| image: optim-ai-bre/backend:latest | |
| container_name: bre-backend | |
| restart: unless-stopped | |
| networks: | |
| - bre-internal | |
| environment: | |
| ASPNETCORE_ENVIRONMENT: Production | |
| ASPNETCORE_URLS: http://+:8080 | |
| DOTNET_RUNNING_IN_CONTAINER: "true" | |
| # Database — reads from .env file | |
| ConnectionStrings__PostgreSQL: >- | |
| Host=bre-postgres;Port=5432; | |
| Database=${POSTGRES_DB:-optimai_bre}; | |
| Username=${POSTGRES_USER:-optimai}; | |
| Password=${POSTGRES_PASSWORD}; | |
| Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100; | |
| Connection Lifetime=300;Keepalive=60 | |
| ConnectionStrings__Redis: >- | |
| bre-redis:6379, | |
| password=${REDIS_PASSWORD}, | |
| abortConnect=false, | |
| connectRetry=3, | |
| connectTimeout=5000, | |
| syncTimeout=5000 | |
| # JWT | |
| Jwt__SecretKey: ${JWT_SECRET_KEY} | |
| Jwt__Issuer: ${JWT_ISSUER:-OptimAI.BRE} | |
| Jwt__Audience: ${JWT_AUDIENCE:-OptimAI.BRE.Clients} | |
| Jwt__AccessTokenExpiryMinutes: ${JWT_ACCESS_TOKEN_EXPIRY_MINUTES:-480} | |
| Jwt__RefreshTokenExpiryDays: ${JWT_REFRESH_TOKEN_EXPIRY_DAYS:-30} | |
| # AI Service | |
| AiOptions__UseAzureOpenAI: ${USE_AZURE_OPENAI:-false} | |
| AiOptions__Endpoint: ${AZURE_OPENAI_ENDPOINT:-} | |
| AiOptions__ApiKey: ${AZURE_OPENAI_KEY:-} | |
| AiOptions__ModelName: ${AZURE_OPENAI_MODEL:-gpt-4o} | |
| # CORS | |
| AllowedOrigins__0: ${APP_DOMAIN:-http://localhost} | |
| AllowedOrigins__1: http://bre-frontend:3000 | |
| # Rule Engine | |
| RuleEngine__MaxConcurrentExecutions: ${RULE_ENGINE_MAX_CONCURRENT:-100} | |
| RuleEngine__DefaultTimeoutMs: ${RULE_ENGINE_TIMEOUT_MS:-5000} | |
| RuleEngine__EnableAiByDefault: "false" | |
| RuleEngine__CacheRulesTtlMinutes: ${RULE_ENGINE_CACHE_TTL_MINUTES:-5} | |
| # Serilog | |
| Serilog__MinimumLevel__Default: ${LOG_LEVEL:-Information} | |
| volumes: | |
| - backend-logs:/app/logs | |
| - backend-reports:/app/reports | |
| depends_on: | |
| bre-postgres: | |
| condition: service_healthy | |
| bre-redis: | |
| condition: service_healthy | |
| healthcheck: | |
| test: ["CMD", "curl", "-f", "http://localhost:8080/health"] | |
| interval: 30s | |
| timeout: 15s | |
| retries: 5 | |
| start_period: 60s | |
| logging: | |
| driver: json-file | |
| options: | |
| max-size: "20m" | |
| max-file: "10" | |
| # ------------------------------------------------------------ | |
| # FRONTEND — Next.js 14 UI | |
| # Internal only — nginx proxies / to this service | |
| # NEXT_PUBLIC_API_URL=/api/v1 → nginx routes to backend | |
| # ------------------------------------------------------------ | |
| bre-frontend: | |
| build: | |
| context: ./src/frontend/optim-ai-bre-ui | |
| dockerfile: ../../../docker/Dockerfile.frontend | |
| args: | |
| NEXT_PUBLIC_API_URL: /api/v1 | |
| image: optim-ai-bre/frontend:latest | |
| container_name: bre-frontend | |
| restart: unless-stopped | |
| networks: | |
| - bre-internal | |
| environment: | |
| NODE_ENV: production | |
| NEXT_TELEMETRY_DISABLED: "1" | |
| PORT: "3000" | |
| HOSTNAME: "0.0.0.0" | |
| healthcheck: | |
| test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"] | |
| interval: 30s | |
| timeout: 10s | |
| retries: 3 | |
| start_period: 30s | |
| logging: | |
| driver: json-file | |
| options: | |
| max-size: "10m" | |
| max-file: "5" | |
| # ------------------------------------------------------------ | |
| # POSTGRESQL 15 — Primary Database | |
| # Persistent volume: bre_postgres_data | |
| # Init scripts auto-run on first empty volume start | |
| # ------------------------------------------------------------ | |
| bre-postgres: | |
| image: postgres:${POSTGRES_VERSION:-15-alpine} | |
| container_name: bre-postgres | |
| restart: unless-stopped | |
| networks: | |
| - bre-internal | |
| environment: | |
| POSTGRES_DB: ${POSTGRES_DB:-optimai_bre} | |
| POSTGRES_USER: ${POSTGRES_USER:-optimai} | |
| POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} | |
| POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C" | |
| PGDATA: /var/lib/postgresql/data/pgdata | |
| volumes: | |
| # Persistent data volume | |
| - postgres-data:/var/lib/postgresql/data | |
| # Init scripts — run automatically on empty volume (first start) | |
| - ./docker/postgres-init:/docker-entrypoint-initdb.d/init:ro | |
| - ./database/migrations:/docker-entrypoint-initdb.d/migrations:ro | |
| # Backup folder — mapped to host for easy backup access | |
| - db-backups:/backups | |
| healthcheck: | |
| test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-optimai} -d ${POSTGRES_DB:-optimai_bre}"] | |
| interval: 10s | |
| timeout: 5s | |
| retries: 10 | |
| start_period: 30s | |
| command: > | |
| postgres | |
| -c max_connections=200 | |
| -c shared_buffers=256MB | |
| -c effective_cache_size=768MB | |
| -c maintenance_work_mem=64MB | |
| -c checkpoint_completion_target=0.9 | |
| -c wal_buffers=16MB | |
| -c default_statistics_target=100 | |
| -c random_page_cost=1.1 | |
| -c effective_io_concurrency=200 | |
| -c work_mem=4MB | |
| -c min_wal_size=1GB | |
| -c max_wal_size=4GB | |
| -c log_min_duration_statement=1000 | |
| -c log_line_prefix='%t [%p] %u@%d ' | |
| logging: | |
| driver: json-file | |
| options: | |
| max-size: "10m" | |
| max-file: "5" | |
| # ------------------------------------------------------------ | |
| # REDIS 7 — Rule Cache & Session Store | |
| # Persistent volume: bre_redis_data | |
| # Password-protected in production | |
| # ------------------------------------------------------------ | |
| bre-redis: | |
| image: redis:${REDIS_VERSION:-7-alpine} | |
| container_name: bre-redis | |
| restart: unless-stopped | |
| networks: | |
| - bre-internal | |
| command: > | |
| redis-server | |
| --requirepass ${REDIS_PASSWORD} | |
| --maxmemory 512mb | |
| --maxmemory-policy allkeys-lru | |
| --save 900 1 | |
| --save 300 10 | |
| --save 60 10000 | |
| --appendonly yes | |
| --appendfsync everysec | |
| --loglevel notice | |
| volumes: | |
| - redis-data:/data | |
| healthcheck: | |
| test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] | |
| interval: 10s | |
| timeout: 5s | |
| retries: 5 | |
| start_period: 10s | |
| logging: | |
| driver: json-file | |
| options: | |
| max-size: "5m" | |
| max-file: "3" | |
| # ------------------------------------------------------------ | |
| # DB BACKUP — Scheduled PostgreSQL backup (runs daily at 2AM) | |
| # Backups stored in the db-backups volume | |
| # Keep 7 days of backups | |
| # ------------------------------------------------------------ | |
| bre-db-backup: | |
| image: postgres:${POSTGRES_VERSION:-15-alpine} | |
| container_name: bre-db-backup | |
| restart: unless-stopped | |
| networks: | |
| - bre-internal | |
| environment: | |
| PGPASSWORD: ${POSTGRES_PASSWORD} | |
| POSTGRES_DB: ${POSTGRES_DB:-optimai_bre} | |
| POSTGRES_USER: ${POSTGRES_USER:-optimai} | |
| volumes: | |
| - db-backups:/backups | |
| entrypoint: > | |
| sh -c ' | |
| while true; do | |
| TIMESTAMP=$$(date +%Y%m%d_%H%M%S) | |
| BACKUP_FILE="/backups/bre_backup_$$TIMESTAMP.sql.gz" | |
| echo "[$$TIMESTAMP] Starting database backup..." | |
| pg_dump -h bre-postgres -U $$POSTGRES_USER -d $$POSTGRES_DB | gzip > $$BACKUP_FILE | |
| echo "[$$TIMESTAMP] Backup saved: $$BACKUP_FILE" | |
| # Delete backups older than 7 days | |
| find /backups -name "bre_backup_*.sql.gz" -mtime +7 -delete | |
| echo "[$$TIMESTAMP] Old backups cleaned. Sleeping 24h..." | |
| sleep 86400 | |
| done | |
| ' | |
| depends_on: | |
| bre-postgres: | |
| condition: service_healthy | |
| logging: | |
| driver: json-file | |
| options: | |
| max-size: "5m" | |
| max-file: "3" | |