zerocodebre / docker-compose.yml
zombee11's picture
Upload 59 files
ec24e1d verified
Raw
History Blame Contribute Delete
10.5 kB
# ==============================================================
# 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"