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: - "127.0.0.1:8080:8080" depends_on: valkey: condition: service_healthy restart: unless-stopped # ── Vector database ── chromadb: image: chromadb/chroma:latest volumes: - chromadb-data:/chroma/chroma ports: - "127.0.0.1:8000:8000" environment: - IS_PERSISTENT=TRUE - ANONYMIZED_TELEMETRY=FALSE restart: unless-stopped # ── LLM API proxy ── litellm: image: ghcr.io/berriai/litellm:main-latest command: ["--config", "/app/config.yaml", "--port", "4000"] volumes: - ./litellm/config.yaml:/app/config.yaml:ro ports: - "127.0.0.1:4000:4000" environment: - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY} - 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} 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 # ── Chat UI ── open-webui: image: ghcr.io/open-webui/open-webui:main volumes: - open-webui-data:/app/backend/data ports: - "127.0.0.1:3000:8080" environment: - OLLAMA_BASE_URL= - OPENAI_API_BASE_URL=http://litellm:4000/v1 - OPENAI_API_KEY=${LITELLM_MASTER_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: litellm: 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 volumes: valkey-data: chromadb-data: open-webui-data: tailscale-state: