From f202a39e5294b3176fba6ac6cdfccb9906c81ae6 Mon Sep 17 00:00:00 2001 From: Ray Andrew Date: Fri, 13 Feb 2026 00:10:01 -0600 Subject: [PATCH] chore: initialize --- .env.example | 27 ++++++++++ .gitignore | 5 ++ cloud-init.yml | 31 +++++++++++ docker-compose.yml | 122 +++++++++++++++++++++++++++++++++++++++++++ litellm/config.yaml | 64 +++++++++++++++++++++++ searxng/settings.yml | 52 ++++++++++++++++++ 6 files changed, 301 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 cloud-init.yml create mode 100644 docker-compose.yml create mode 100644 litellm/config.yaml create mode 100644 searxng/settings.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..499d6f8 --- /dev/null +++ b/.env.example @@ -0,0 +1,27 @@ +# ============================================ +# Hetzner Self-Hosted Stack — Environment Variables +# Copy to .env and fill in your values: +# cp .env.example .env +# ============================================ + +# --- LiteLLM --- +LITELLM_MASTER_KEY=sk-change-me-to-a-random-string +OPENROUTER_API_KEY=sk-or-... +SILICONFLOW_API_KEY=sk-... +DEEPINFRA_API_KEY=... +GROQ_API_KEY=gsk_... +CEREBRAS_API_KEY=... + +# --- Cloudflare Tunnel --- +# Create a tunnel in Cloudflare Zero Trust dashboard → Networks → Tunnels +# Copy the token from the tunnel install command +CLOUDFLARE_TUNNEL_TOKEN=eyJ... + +# --- Tailscale --- +# Generate at https://login.tailscale.com/admin/settings/keys +# Use a reusable + ephemeral key for unattended servers +TS_AUTHKEY=tskey-auth-... + +# --- SearXNG --- +# Random secret key for SearXNG (generate with: openssl rand -hex 32) +SEARXNG_SECRET_KEY=change-me-to-a-random-hex-string diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0fb32d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Environment secrets +.env + +# SearXNG runtime state +searxng/uwsgi.ini diff --git a/cloud-init.yml b/cloud-init.yml new file mode 100644 index 0000000..a1f257a --- /dev/null +++ b/cloud-init.yml @@ -0,0 +1,31 @@ +#cloud-config + +users: + - name: rayandrew + groups: [sudo, docker] + shell: /bin/bash + sudo: ALL=(ALL) NOPASSWD:ALL + ssh_authorized_keys: + - ssh-import-id gh:rayandrew + +package_update: true +package_upgrade: true + +packages: + - curl + - git + - unattended-upgrades + +runcmd: + # Install Docker via official convenience script + - curl -fsSL https://get.docker.com | sh + - systemctl enable docker + - systemctl start docker + + # Clone repo and set ownership + - git clone https://git.rs.ht/rayandrew/ai-servers.git /home/rayandrew/ai-servers + - chown -R rayandrew:rayandrew /home/rayandrew/ai-servers + + # Enable automatic security updates + - systemctl enable unattended-upgrades + - systemctl start unattended-upgrades diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b2d7915 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,122 @@ +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 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/heartbeat"] + interval: 15s + timeout: 5s + retries: 3 + + # ── 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", "curl", "-f", "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 + chromadb: + 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: hetzner-stack + 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: diff --git a/litellm/config.yaml b/litellm/config.yaml new file mode 100644 index 0000000..0d46b11 --- /dev/null +++ b/litellm/config.yaml @@ -0,0 +1,64 @@ +model_list: + # --- OpenRouter models --- + - model_name: kimi-k2.5 + litellm_params: + model: openrouter/moonshotai/kimi-k2.5 + api_key: os.environ/OPENROUTER_API_KEY + + - model_name: devstral + litellm_params: + model: openrouter/mistralai/devstral-small + api_key: os.environ/OPENROUTER_API_KEY + + - model_name: minimax-m2 + litellm_params: + model: openrouter/minimax/minimax-m1 + api_key: os.environ/OPENROUTER_API_KEY + + - model_name: gpt-oss + litellm_params: + model: openrouter/openai/gpt-4.1-mini + api_key: os.environ/OPENROUTER_API_KEY + + # --- SiliconFlow models --- + - model_name: glm-4.7 + litellm_params: + model: openai/THUDM/GLM-4-32B-0414 + api_base: https://api.siliconflow.cn/v1 + api_key: os.environ/SILICONFLOW_API_KEY + + - model_name: qwen3-coder + litellm_params: + model: openai/Qwen/Qwen3-Coder + api_base: https://api.siliconflow.cn/v1 + api_key: os.environ/SILICONFLOW_API_KEY + + # --- DeepInfra models --- + - model_name: deepseek-v3.2 + litellm_params: + model: deepinfra/deepseek-ai/DeepSeek-V3-0324 + api_key: os.environ/DEEPINFRA_API_KEY + + - model_name: devstral-deepinfra + litellm_params: + model: deepinfra/mistralai/Devstral-Small-2505 + api_key: os.environ/DEEPINFRA_API_KEY + + # --- Groq (free/fast) --- + - model_name: llama-3.3-70b + litellm_params: + model: groq/llama-3.3-70b-versatile + api_key: os.environ/GROQ_API_KEY + + # --- Cerebras (free/fast) --- + - model_name: llama-3.3-70b-cerebras + litellm_params: + model: cerebras/llama-3.3-70b + api_key: os.environ/CEREBRAS_API_KEY + +general_settings: + master_key: os.environ/LITELLM_MASTER_KEY + +litellm_settings: + drop_params: true + set_verbose: false diff --git a/searxng/settings.yml b/searxng/settings.yml new file mode 100644 index 0000000..4ec09d3 --- /dev/null +++ b/searxng/settings.yml @@ -0,0 +1,52 @@ +use_default_settings: true + +general: + instance_name: "SearXNG" + privacypolicy_url: false + donation_url: false + enable_metrics: false + +search: + safe_search: 0 + autocomplete: "google" + formats: + - html + - json + +server: + secret_key: "${SEARXNG_SECRET_KEY}" + limiter: false + image_proxy: true + public_instance: false + +ui: + static_use_hash: true + +engines: + - name: google + engine: google + shortcut: g + + - name: duckduckgo + engine: duckduckgo + shortcut: ddg + + - name: brave + engine: brave + shortcut: br + + - name: wikipedia + engine: wikipedia + shortcut: wp + + - name: github + engine: github + shortcut: gh + + - name: stackoverflow + engine: stackoverflow + shortcut: so + + - name: arxiv + engine: arxiv + shortcut: arx