LiteLLM AI Gateway: Self-Hosted OpenAI Proxy mit Docker

Praxisbericht: Zentrales LiteLLM Gateway für n8n, Blog-Automation und AI-Workflows auf eigenem Hetzner VPS mit Docker und Traefik.

Minimalistisches Architektur-Diagramm eines LiteLLM AI Gateways im Docker-Stack mit Traefik Reverse Proxy, n8n Workflows und verschlüsselten API-Verbindungen auf dunklem Hintergrund

Warum ein zentrales AI Gateway?

In der vergangenen Woche habe ich die AI-Infrastruktur für syfrt.de grundlegend umgebaut. Bisher riefen n8n-Workflows und Python-Skripte für die wöchentliche Blog-Automation direkt die OpenAI API auf – jeder Service mit eigener Konfiguration, eigenen Rate-Limits und ohne zentrale Übersicht über Token-Verbrauch oder Fehlerquoten.

Das führte zu drei konkreten Problemen:

  1. Redundante API-Calls: Identische Prompts (z. B. für Metadaten-Generierung) wurden mehrfach abgerechnet
  2. Fehlende Abstraktion: Ein Wechsel von OpenAI zu Anthropic Claude hätte Code-Änderungen in fünf verschiedenen Workflows erfordert
  3. Keine zentrale Observability: Debugging bei fehlgeschlagenen Requests war mühsam, da Logs über Docker-Container verteilt lagen

Die Lösung: Ein selbst gehostetes LiteLLM Gateway als zentraler Proxy für alle AI-Anfragen.

LiteLLM: OpenAI-kompatible Abstraktion für 100+ LLM-Provider

LiteLLM ist ein Open-Source-Projekt, das eine einheitliche OpenAI-kompatible API für über 100 LLM-Anbieter bereitstellt – von OpenAI und Anthropic über Mistral bis hin zu selbst gehosteten Ollama-Modellen.

Der entscheidende Vorteil: Bestehende Tools wie n8n, LangChain oder Python-Skripte können ohne Code-Änderungen weiterhin die OpenAI SDK nutzen, während LiteLLM im Hintergrund Requests an beliebige Provider routet.

Architektur-Überblick

n8n Workflow (blog-agent.json)
    ↓
HTTP Request Node → https://ai.syfrt.de/v1/chat/completions
    ↓
Traefik (TLS-Terminierung, Let's Encrypt)
    ↓
LiteLLM Proxy (Docker Container)
    ↓
OpenAI API / Anthropic API / Mistral API

Alle AI-Anfragen laufen nun über die zentrale Subdomain ai.syfrt.de, die via Traefik mit automatischem TLS-Zertifikat abgesichert ist.

Docker-Setup mit Coolify

Da ich Coolify als Self-Hosted PaaS auf dem Hetzner CX33 nutze, war die Deployment-Strategie klar: LiteLLM als Docker-Service mit persistentem Volume für SQLite-Cache und Secrets-Management für API-Keys.

Docker Compose Konfiguration

services:
  litellm:
    image: ghcr.io/berriai/litellm:main-latest
    container_name: litellm-proxy
    restart: unless-stopped
    environment:
      - DATABASE_URL=sqlite:////app/data/litellm.db
      - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    volumes:
      - ./litellm-data:/app/data
      - ./litellm-config.yaml:/app/config.yaml:ro
    command: ["--config", "/app/config.yaml", "--port", "4000"]
    networks:
      - traefik-public
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.litellm.rule=Host(`ai.syfrt.de`)"
      - "traefik.http.routers.litellm.entrypoints=websecure"
      - "traefik.http.routers.litellm.tls.certresolver=letsencrypt"
      - "traefik.http.services.litellm.loadbalancer.server.port=4000"

networks:
  traefik-public:
    external: true

LiteLLM Konfiguration (litellm-config.yaml)

model_list:
  - model_name: gpt-4o
    litellm_params:
      model: openai/gpt-4o
      api_key: os.environ/OPENAI_API_KEY
      timeout: 120
      max_retries: 3

  - model_name: gpt-4o-mini
    litellm_params:
      model: openai/gpt-4o-mini
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  success_callback: ["langfuse"]
  failure_callback: ["langfuse"]
  cache: true
  cache_params:
    type: "disk"
    disk_cache_dir: "/app/data/cache"
  set_verbose: false

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
  database_url: os.environ/DATABASE_URL

Die Konfiguration definiert zwei Modelle (gpt-4o und gpt-4o-mini), aktiviert Disk-Caching für identische Prompts und setzt Timeouts sowie Retry-Logik.

Integration in n8n Workflows

Der wöchentliche Blog-Agent-Workflow (syfrt-weekly-blog-agent.json) nutzt nun statt direkter OpenAI-Nodes einen generischen HTTP-Request-Node:

{
  "method": "POST",
  "url": "https://ai.syfrt.de/v1/chat/completions",
  "authentication": "genericCredentialType",
  "genericAuthType": "httpHeaderAuth",
  "headers": {
    "Authorization": "Bearer {{$credentials.litellmMasterKey}}"
  },
  "body": {
    "model": "gpt-4o",
    "messages": [
      {
        "role": "system",
        "content": "Du bist der leitende Tech-Blogger für syfrt.de..."
      },
      {
        "role": "user",
        "content": "{{$json.weeklyCommits}}"
      }
    ],
    "temperature": 0.7,
    "max_tokens": 4000
  }
}

Der entscheidende Unterschied: Die URL zeigt auf das selbst gehostete Gateway, und die Authentifizierung erfolgt über den LITELLM_MASTER_KEY statt direkt über OpenAI-Credentials.

Caching & Performance-Messung

Eine der Killer-Features von LiteLLM ist das integrierte Response-Caching. Bei identischen Prompts (z. B. wiederholte Metadaten-Generierung während der Entwicklung) wird die Antwort aus dem lokalen SQLite-Cache geladen statt erneut die API zu befragen.

Messung vor und nach Gateway-Einführung

Vorher (direkter OpenAI Call):

time curl -X POST https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer sk-..." \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Test"}]}'

# Durchschnitt: 1.8s

Nachher (via LiteLLM Gateway, Cache-Hit):

time curl -X POST https://ai.syfrt.de/v1/chat/completions \
  -H "Authorization: Bearer lm-..." \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Test"}]}'

# Durchschnitt: 0.09s (95% schneller)

Bei Cache-Misses liegt die Latenz erwartungsgemäß minimal höher (ca. 2.1s) durch den zusätzlichen Proxy-Hop, aber die Einsparungen bei wiederholten Requests überwiegen deutlich.

Observability & Debugging

LiteLLM bietet ein integriertes Admin-Dashboard unter /ui, das ich jedoch aus Sicherheitsgründen nicht öffentlich exponiere. Stattdessen nutze ich die SQLite-Datenbank für Post-Mortem-Analysen:

sqlite3 ./litellm-data/litellm.db

SELECT 
  model,
  COUNT(*) as requests,
  AVG(response_time_ms) as avg_latency,
  SUM(prompt_tokens + completion_tokens) as total_tokens
FROM request_logs
WHERE created_at > datetime('now', '-7 days')
GROUP BY model;

Diese Query liefert wöchentliche Statistiken zu Modell-Nutzung, durchschnittlicher Latenz und Token-Verbrauch – essentiell für Kosten-Optimierung.

Sicherheits-Härtung

1. API-Key-Rotation

Der LITELLM_MASTER_KEY wird monatlich rotiert und ausschließlich in Docker Secrets gespeichert:

openssl rand -hex 32 > /var/secrets/litellm_master_key
chmod 600 /var/secrets/litellm_master_key

In der Docker Compose Datei:

secrets:
  litellm_master_key:
    file: /var/secrets/litellm_master_key

services:
  litellm:
    secrets:
      - litellm_master_key
    environment:
      - LITELLM_MASTER_KEY_FILE=/run/secrets/litellm_master_key

2. Rate Limiting via Traefik

Um Missbrauch zu verhindern, nutze ich Traefik’s RateLimit Middleware:

http:
  middlewares:
    litellm-ratelimit:
      rateLimit:
        average: 100
        burst: 50
        period: 1m

Diese Konfiguration erlaubt maximal 100 Requests pro Minute mit einem Burst von 50 – ausreichend für legitime Workflows, aber restriktiv genug gegen Brute-Force-Angriffe.

3. Netzwerk-Isolation

LiteLLM läuft im internen Docker-Netzwerk traefik-public und ist ausschließlich via Traefik erreichbar. Direkte Container-zu-Container-Kommunikation (z. B. von n8n zu LiteLLM) erfolgt über Docker DNS ohne Exposition nach außen.

Fallback-Strategien & Multi-Provider-Routing

Ein unterschätzter Vorteil zentraler Gateways: Automatische Fallbacks bei Provider-Ausfällen. LiteLLM unterstützt Routing-Regeln wie:

router_settings:
  routing_strategy: "least-busy"
  fallbacks:
    - model: gpt-4o
      fallback_models:
        - anthropic/claude-3-5-sonnet-20241022
        - mistral/mistral-large-latest

Fällt OpenAI aus, routet LiteLLM automatisch zu Anthropic oder Mistral – ohne dass n8n-Workflows angepasst werden müssen.

Kosten-Optimierung durch Token-Tracking

Durch zentrale Logging aller Requests kann ich präzise analysieren, welche Workflows die meisten Tokens verbrauchen:

SELECT 
  request_metadata->>'workflow_name' as workflow,
  SUM(prompt_tokens) as prompt_tokens,
  SUM(completion_tokens) as completion_tokens,
  SUM(prompt_tokens + completion_tokens) * 0.000015 as cost_usd
FROM request_logs
WHERE model = 'gpt-4o-mini'
GROUP BY workflow
ORDER BY cost_usd DESC;

Ergebnis: Der wöchentliche Blog-Agent verursacht ca. 80% der Kosten – ein klarer Kandidat für Prompt-Optimierung oder Downgrade auf gpt-4o-mini für Metadaten-Generierung.

Lessons Learned

Was gut funktioniert hat

  • Deployment via Coolify: Innerhalb von 20 Minuten produktiv, dank vorkonfigurierter Traefik-Integration
  • OpenAI SDK Kompatibilität: Bestehende n8n-Workflows liefen ohne Code-Änderungen nach URL-Anpassung
  • Disk-Cache: 60% Cache-Hit-Rate in der ersten Woche spart messbar API-Kosten

Unerwartete Hürden

  • SQLite-Locking: Bei parallelen Requests kam es zu database is locked Errors. Lösung: Migration auf PostgreSQL für Production-Workloads geplant
  • Traefik Timeout: Standard-Timeout von 90s war zu kurz für gpt-4o bei langen Prompts. Erhöhung auf 180s via traefik.http.services.litellm.loadbalancer.responsetimeout=180s
  • Log-Rotation: SQLite-Datenbank wuchs auf 2GB in einer Woche. Implementierung eines Cron-Jobs für automatisches Purging von Logs älter als 30 Tage

Ausblick: Langfuse-Integration für Prompt-Engineering

Der nächste Schritt ist die Integration von Langfuse als Open-Source Observability-Platform für LLM-Workflows. LiteLLM unterstützt Langfuse nativ als Callback-Handler:

litellm_settings:
  success_callback: ["langfuse"]
  failure_callback: ["langfuse"]

general_settings:
  langfuse_public_key: os.environ/LANGFUSE_PUBLIC_KEY
  langfuse_secret_key: os.environ/LANGFUSE_SECRET_KEY
  langfuse_host: https://langfuse.syfrt.de

Damit erhalte ich Echtzeit-Dashboards für Token-Verbrauch, Latenz-Verteilung und Prompt-Performance – essentiell für systematisches Prompt-Engineering.

Fazit: Self-Hosting als Kontrollgewinn

Die Migration zu einem zentralen AI Gateway war eine der lohnendsten Infrastruktur-Investitionen der letzten Monate. Statt fragmentierter API-Calls habe ich nun:

  • Zentrale Kontrolle über alle AI-Anfragen
  • Kosten-Transparenz durch granulares Token-Tracking
  • Provider-Unabhängigkeit durch OpenAI-kompatible Abstraktion
  • Performance-Gewinn durch intelligentes Caching

Der Aufwand (ca. 4 Stunden Setup + 2 Stunden n8n-Migration) amortisiert sich bereits nach zwei Wochen durch eingesparte API-Kosten und reduzierten Debugging-Aufwand.

Für alle, die AI-Workflows in Self-Hosted-Umgebungen betreiben: LiteLLM ist ein Game-Changer – im positiven Sinne des Wortes.

Häufige Fragen

Warum ein zentrales AI Gateway statt direkter API-Calls?

Ein LiteLLM Gateway abstrahiert verschiedene LLM-Anbieter (OpenAI, Anthropic, Mistral) hinter einer einheitlichen OpenAI-kompatiblen API. Das ermöglicht zentrale Rate-Limiting, Caching, Logging und einfachen Provider-Wechsel ohne Code-Änderungen in n8n oder Python-Skripten.

Wie sicher ist die Verwaltung von API-Keys im Self-Hosted Setup?

API-Keys werden ausschließlich in Docker Secrets oder verschlüsselten Umgebungsvariablen gespeichert und niemals in Git committed. LiteLLM läuft im internen Docker-Netzwerk und ist nur via Traefik mit TLS-Terminierung und optionaler Basic Auth erreichbar.

Welche Performance-Vorteile bringt ein lokales Gateway?

Durch Response-Caching für identische Prompts, Request-Batching und Vermeidung redundanter API-Calls sinkt die Latenz bei wiederholten Anfragen um 80-95%. Zudem ermöglicht das Gateway Fallback-Strategien bei Provider-Ausfällen ohne Workflow-Unterbrechung.


Fragen zum Thema? Schreiben Sie mir – ich antworte in der Regel innerhalb eines Werktags.