services: # ── Cache for SearXNG ── valkey: image: valkey/valkey:8-alpine command: valkey-server --save 30 1 --loglevel warning volumes: - valkey-data:/data restart: unless-stopped healthcheck: test: ["CMD", "valkey-cli", "ping"] interval: 10s timeout: 3s retries: 3 # ── Private search engine ── searxng: image: searxng/searxng:latest volumes: - ./searxng:/etc/searxng:rw environment: - SEARXNG_SECRET_KEY=${SEARXNG_SECRET_KEY} ports: - "0.0.0.0:8080:8080" depends_on: valkey: condition: service_healthy restart: unless-stopped # ── Vector database ── chromadb: image: chromadb/chroma:latest volumes: - chromadb-data:/chroma/chroma ports: - "0.0.0.0:8000:8000" environment: - IS_PERSISTENT=TRUE - ANONYMIZED_TELEMETRY=FALSE restart: unless-stopped # # ── Database for LiteLLM (DEPRECATED — kept for rollback) ── # litellm-db: # image: postgres:16-alpine # volumes: # - litellm-db-data:/var/lib/postgresql/data # environment: # - POSTGRES_DB=litellm # - POSTGRES_USER=litellm # - POSTGRES_PASSWORD=${LITELLM_DB_PASSWORD} # restart: unless-stopped # healthcheck: # test: ["CMD-SHELL", "pg_isready -U litellm"] # interval: 10s # timeout: 3s # retries: 3 # # ── LLM API proxy (DEPRECATED — replaced by new-api) ── # litellm: # image: ghcr.io/berriai/litellm:main-latest # command: ["--config", "/app/config.yaml", "--port", "4000"] # volumes: # - ./litellm/config.yaml:/app/config.yaml:ro # ports: # - "0.0.0.0:4000:4000" # environment: # - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY} # - DATABASE_URL=postgresql://litellm:${LITELLM_DB_PASSWORD}@litellm-db:5432/litellm # - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} # - SILICONFLOW_API_KEY=${SILICONFLOW_API_KEY} # - DEEPINFRA_API_KEY=${DEEPINFRA_API_KEY} # - GROQ_API_KEY=${GROQ_API_KEY} # - CEREBRAS_API_KEY=${CEREBRAS_API_KEY} # depends_on: # litellm-db: # condition: service_healthy # restart: unless-stopped # healthcheck: # test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:4000/health/liveliness')"] # interval: 15s # timeout: 5s # retries: 5 # start_period: 30s # ── LLM API proxy (new-api) ── new-api: image: calciumion/new-api:latest ports: - "0.0.0.0:4000:3000" volumes: - new-api-data:/data environment: - SQL_DSN= - TZ=UTC - ENABLE_METRIC=true - INITIAL_ROOT_ACCESS_TOKEN=${NEW_API_ACCESS_TOKEN} restart: unless-stopped healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/status"] interval: 15s timeout: 5s retries: 5 start_period: 10s # ── Chat UI ── open-webui: image: ghcr.io/open-webui/open-webui:main volumes: - open-webui-data:/app/backend/data ports: - "0.0.0.0:3000:8080" environment: - OLLAMA_BASE_URL= - OPENAI_API_BASE_URL=http://new-api:3000/v1 - OPENAI_API_KEY=${OPENWEBUI_API_KEY} - ENABLE_RAG_WEB_SEARCH=true - RAG_WEB_SEARCH_ENGINE=searxng - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json - CHROMA_HTTP_HOST=chromadb - CHROMA_HTTP_PORT=8000 - WEBUI_AUTH=true depends_on: new-api: condition: service_healthy restart: unless-stopped # ── Cloudflare Tunnel (public HTTPS) ── cloudflared: image: cloudflare/cloudflared:latest command: tunnel run environment: - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN} restart: unless-stopped # ── Tailscale (private VPN access) ── tailscale: image: tailscale/tailscale:latest hostname: ai-proxy volumes: - tailscale-state:/var/lib/tailscale - /dev/net/tun:/dev/net/tun cap_add: - NET_ADMIN - SYS_MODULE environment: - TS_AUTHKEY=${TS_AUTHKEY} - TS_STATE_DIR=/var/lib/tailscale - TS_USERSPACE=false restart: unless-stopped network_mode: host # ═══════════════════════════════════════════════ # Monitoring stack # ═══════════════════════════════════════════════ # ── Metrics store (Prometheus-compatible) ── victoriametrics: image: victoriametrics/victoria-metrics:latest volumes: - victoriametrics-data:/victoria-metrics-data - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro command: - "-promscrape.config=/etc/prometheus/prometheus.yml" - "-retentionPeriod=90d" - "-storageDataPath=/victoria-metrics-data" ports: - "127.0.0.1:8428:8428" restart: unless-stopped # ── Dashboards ── grafana: image: grafana/grafana:latest volumes: - grafana-data:/var/lib/grafana ports: - "0.0.0.0:3001:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD} - GF_USERS_ALLOW_SIGN_UP=false depends_on: - victoriametrics restart: unless-stopped # ── Host system metrics ── node-exporter: image: prom/node-exporter:latest pid: host volumes: - /proc:/host/proc:ro - /sys:/host/sys:ro - /:/rootfs:ro command: - "--path.procfs=/host/proc" - "--path.sysfs=/host/sys" - "--path.rootfs=/rootfs" - "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)" restart: unless-stopped # ── Valkey/Redis metrics ── redis-exporter: image: oliver006/redis_exporter:latest environment: - REDIS_ADDR=redis://valkey:6379 depends_on: valkey: condition: service_healthy restart: unless-stopped volumes: valkey-data: chromadb-data: litellm-db-data: new-api-data: open-webui-data: tailscale-state: victoriametrics-data: grafana-data: