services: postgres: image: postgis/postgis:16-3.4 container_name: goodgo-postgres restart: unless-stopped ports: - '${DB_PORT:-5432}:5432' environment: POSTGRES_DB: ${DB_NAME:-goodgo} POSTGRES_USER: ${DB_USER:-goodgo} POSTGRES_PASSWORD: ${DB_PASSWORD:-goodgo_secret} volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ['CMD-SHELL', 'pg_isready -U ${DB_USER:-goodgo} -d ${DB_NAME:-goodgo}'] interval: 10s timeout: 5s retries: 5 start_period: 30s networks: - goodgo-net redis: image: redis:7-alpine container_name: goodgo-redis restart: unless-stopped ports: - '${REDIS_PORT:-6379}:6379' command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru volumes: - redis_data:/data healthcheck: test: ['CMD', 'redis-cli', 'ping'] interval: 10s timeout: 5s retries: 5 start_period: 10s networks: - goodgo-net typesense: image: typesense/typesense:27.1 container_name: goodgo-typesense restart: unless-stopped ports: - '${TYPESENSE_PORT:-8108}:8108' environment: TYPESENSE_API_KEY: ${TYPESENSE_API_KEY:-ts_dev_key_change_me} TYPESENSE_DATA_DIR: /data TYPESENSE_ENABLE_CORS: 'true' volumes: - typesense_data:/data healthcheck: test: ['CMD', 'curl', '-sf', 'http://localhost:8108/health'] interval: 10s timeout: 5s retries: 5 start_period: 15s networks: - goodgo-net minio: image: minio/minio:latest container_name: goodgo-minio restart: unless-stopped ports: - '${MINIO_API_PORT:-9000}:9000' - '${MINIO_CONSOLE_PORT:-9001}:9001' command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:?MINIO_ACCESS_KEY is required} MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:?MINIO_SECRET_KEY is required} volumes: - minio_data:/data healthcheck: test: ['CMD', 'mc', 'ready', 'local'] interval: 10s timeout: 5s retries: 5 start_period: 15s networks: - goodgo-net ai-services: build: context: ./libs/ai-services dockerfile: Dockerfile container_name: goodgo-ai-services restart: unless-stopped ports: - '${AI_SERVICES_PORT:-8000}:8000' environment: AI_DEBUG: ${AI_DEBUG:-false} AI_LOG_LEVEL: ${AI_LOG_LEVEL:-info} healthcheck: test: ['CMD', 'python', '-c', 'import httpx; httpx.get("http://localhost:8000/health").raise_for_status()'] interval: 30s timeout: 5s retries: 5 start_period: 30s networks: - goodgo-net # ── Database Backup ── pg-backup: image: postgis/postgis:16-3.4 container_name: goodgo-pg-backup restart: unless-stopped entrypoint: /bin/bash command: - -c - | apt-get update -qq && apt-get install -y -qq cron > /dev/null 2>&1 echo "0 2 * * * PGHOST=postgres PGPORT=5432 PGUSER=${DB_USER:-goodgo} PGDATABASE=${DB_NAME:-goodgo} PGPASSWORD=${DB_PASSWORD:-goodgo_secret} BACKUP_DIR=/backups RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-7} /scripts/pg-backup.sh >> /var/log/pg-backup.log 2>&1" | crontab - /scripts/pg-backup.sh cron -f environment: PGHOST: postgres PGPORT: '5432' PGUSER: ${DB_USER:-goodgo} PGDATABASE: ${DB_NAME:-goodgo} PGPASSWORD: ${DB_PASSWORD:-goodgo_secret} BACKUP_DIR: /backups RETENTION_DAYS: ${BACKUP_RETENTION_DAYS:-7} volumes: - ./scripts/backup:/scripts:ro - pg_backups:/backups depends_on: postgres: condition: service_healthy networks: - goodgo-net # ── Log Aggregation ── loki: image: grafana/loki:3.0.0 container_name: goodgo-loki restart: unless-stopped ports: - '${LOKI_PORT:-3100}:3100' command: -config.file=/etc/loki/loki-config.yml volumes: - ./monitoring/loki/loki-config.yml:/etc/loki/loki-config.yml:ro - loki_data:/loki healthcheck: test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:3100/ready'] interval: 15s timeout: 5s retries: 5 start_period: 20s networks: - goodgo-net promtail: image: grafana/promtail:3.0.0 container_name: goodgo-promtail restart: unless-stopped command: -config.file=/etc/promtail/promtail-config.yml volumes: - ./monitoring/promtail/promtail-config.yml:/etc/promtail/promtail-config.yml:ro - /var/run/docker.sock:/var/run/docker.sock:ro depends_on: loki: condition: service_healthy networks: - goodgo-net prometheus: image: prom/prometheus:v2.51.0 container_name: goodgo-prometheus restart: unless-stopped ports: - '${PROMETHEUS_PORT:-9090}:9090' command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.retention.time=15d' - '--web.enable-lifecycle' volumes: - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus_data:/prometheus extra_hosts: - 'host.docker.internal:host-gateway' healthcheck: test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:9090/-/healthy'] interval: 15s timeout: 5s retries: 3 start_period: 10s networks: - goodgo-net grafana: image: grafana/grafana:10.4.1 container_name: goodgo-grafana restart: unless-stopped ports: - '${GRAFANA_PORT:-3002}:3000' environment: GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin} GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} GF_USERS_ALLOW_SIGN_UP: 'false' volumes: - ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro - grafana_data:/var/lib/grafana depends_on: prometheus: condition: service_healthy loki: condition: service_healthy healthcheck: test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:3000/api/health'] interval: 15s timeout: 5s retries: 3 start_period: 15s networks: - goodgo-net volumes: pgdata: driver: local redis_data: driver: local typesense_data: driver: local minio_data: driver: local pg_backups: driver: local loki_data: driver: local prometheus_data: driver: local grafana_data: driver: local networks: goodgo-net: driver: bridge name: goodgo-net