Files
hermes-config/skills/devops/selfhosted-migration/references/hybrid-vps-home-llm.md
T
2026-07-12 10:17:17 -04:00

2.9 KiB

Hybrid VPS + Home LLM Hosting Pattern

When migrating a web app that has hardcoded local LLM calls to a VPS, keep the LLM at home and proxy calls back through Tailscale. The app code doesn't change — only the nginx routing changes.

When to use

  • The app makes fetch('/llm/...') calls hardcoded to a local Ollama endpoint
  • The LLM requires a GPU (e.g., qwen2.5:14b at ~9GB VRAM) — a $5/mo VPS can't run it
  • You want to move the frontend and database to a VPS (static IP, no home IP exposure)
  • Home server stays on 24/7 anyway (Home Assistant, Immich, other services)

Architecture

VPS ($5/mo)                          Home server (Tailscale)
├── Frontend (SPA)                   ├── Ollama (port 11434)
├── PocketBase / SQLite              └── Tailscale IP: 100.xx.xx.xx
├── Nginx (SSL)
│   ├── /        → frontend
│   ├── /api/    → PocketBase (local on VPS)
│   └── /llm/    → Tailscale → home:11434
└── Tailscale

Nginx Config (VPS)

# Add to existing server block:
location /llm/ {
    proxy_pass http://<home-tailscale-ip>:11434/;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_read_timeout 120s;
    proxy_buffering off;
}

Key properties

  • Zero code changes. The app's fetch('/llm/v1/chat/completions') calls work unchanged — nginx handles the routing.
  • Latency: +2-5ms Tailscale overhead. For non-streaming LLM API calls (50-500 tokens), the added latency is completely irrelevant — the LLM generation time (2-5 seconds) dominates.
  • Bandwidth: LLM API responses are small (JSON, a few KB). Tailscale overhead is negligible.
  • Reliability: If the home server goes down, /llm/ calls fail gracefully (the app should already handle API errors). Frontend and database keep working on the VPS.

Migration checklist

  1. VPS: Install nginx, Docker (for PocketBase if needed), Tailscale
  2. Rsync frontend to VPS
  3. Rsync database (PocketBase pb_data/, SQLite files) to VPS
  4. Tailscale: Authorize VPS on the tailnet, note home server's Tailscale IP
  5. Nginx: Add /llm/ proxy block pointing to home Tailscale IP
  6. SSL: Let's Encrypt cert for the VPS domain
  7. DNS: Point domain to VPS IP
  8. Test: Hit /llm/v1/models to verify Ollama is reachable through the proxy

Example: ShopProQuote

Real-world application (shop management SPA):

Component Size Migrate?
Frontend (HTML/JS/CSS) 110 MB → VPS
PocketBase (SQLite) 17 MB (380 KB DB) → VPS
Ollama (qwen2.5:14b) ~9 GB VRAM → stays home
Nginx config → recreate on VPS

LLM endpoints used: priority analysis, AI Write, repair order suggestions, appointment OCR parsing. All via fetch('/llm/v1/chat/completions') with model: 'qwen2.5:14b'.

Downtime: ~10 minutes during DNS switch. Test on a subdomain first.