# ============================================================================= # @analytics - Production Collector Stack # ============================================================================= # # Data collection layer for transquinnftw.com analytics. # Runs on vps-0 alongside the lilith-platform backend. # # Services: # - TimescaleDB: Time-series analytics storage (port 25434 external) # - Redis: BullMQ job queues # - Collector: Event ingestion POST /collect (port 4001) # - Processor: BullMQ workers (internal) # - API: Query endpoints (port 4003) # - Realtime: WebSocket gateway (port 4004) # # DNS: # analytics.db.transquinnftw.com A → vps-0 IP (connects to port 25434) # # Usage: # cp .env.prod.example .env.prod # # Edit .env.prod with real secrets # docker compose -f docker-compose.prod.yaml --env-file .env.prod up -d # services: timescaledb: image: timescale/timescaledb:2.16.1-pg16 container_name: analytics-timescaledb restart: unless-stopped ports: - "25434:5432" environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} volumes: - analytics-postgres-data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] interval: 10s timeout: 5s retries: 5 networks: - analytics-net redis: image: redis:7.4-alpine container_name: analytics-redis restart: unless-stopped command: - redis-server - --requirepass - "${REDIS_PASSWORD}" - --appendonly - "yes" - --maxmemory - "512MB" - --maxmemory-policy - "noeviction" volumes: - analytics-redis-data:/data healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s timeout: 3s retries: 5 networks: - analytics-net collector: build: context: ../services/collector dockerfile: Dockerfile container_name: analytics-collector restart: unless-stopped ports: - "127.0.0.1:4001:4001" environment: NODE_ENV: production PORT: "4001" REDIS_HOST: redis REDIS_PORT: "6379" REDIS_PASSWORD: ${REDIS_PASSWORD} CORS_ORIGINS: ${CORS_ORIGINS} COLLECTOR_WRITE_KEY: ${COLLECTOR_WRITE_KEY} LOG_LEVEL: info depends_on: redis: condition: service_healthy healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:4001/health || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 10s networks: - analytics-net processor: build: context: ../services/processor dockerfile: Dockerfile container_name: analytics-processor restart: unless-stopped environment: NODE_ENV: production REDIS_HOST: redis REDIS_PORT: "6379" REDIS_PASSWORD: ${REDIS_PASSWORD} DATABASE_HOST: timescaledb DATABASE_PORT: "5432" DATABASE_USER: ${POSTGRES_USER} DATABASE_PASSWORD: ${POSTGRES_PASSWORD} DATABASE_NAME: ${POSTGRES_DB} CONCURRENCY: "5" BATCH_SIZE: "100" depends_on: timescaledb: condition: service_healthy redis: condition: service_healthy networks: - analytics-net api: build: context: ../services/api dockerfile: Dockerfile container_name: analytics-api restart: unless-stopped ports: - "127.0.0.1:4003:4003" environment: NODE_ENV: production PORT: "4003" DATABASE_HOST: timescaledb DATABASE_PORT: "5432" DATABASE_USER: ${POSTGRES_USER} DATABASE_PASSWORD: ${POSTGRES_PASSWORD} DATABASE_NAME: ${POSTGRES_DB} REDIS_HOST: redis REDIS_PORT: "6379" REDIS_PASSWORD: ${REDIS_PASSWORD} API_KEYS: ${API_KEYS} depends_on: timescaledb: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:4003/health || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 10s networks: - analytics-net realtime: build: context: ../services/realtime dockerfile: Dockerfile container_name: analytics-realtime restart: unless-stopped ports: - "127.0.0.1:4004:4004" environment: NODE_ENV: production PORT: "4004" REDIS_HOST: redis REDIS_PORT: "6379" REDIS_PASSWORD: ${REDIS_PASSWORD} depends_on: redis: condition: service_healthy networks: - analytics-net volumes: analytics-postgres-data: name: analytics-postgres-data analytics-redis-data: name: analytics-redis-data networks: analytics-net: name: analytics-net driver: bridge