SSD Nodes Learn Hosting plans →
Anleitungen Matt ConnorVon Matt Connor

LiteLLM selbst hosten: LLM-Gateway einrichten

LiteLLM auf einem VPS als OpenAI-kompatibles Gateway betreiben: virtuelle Schlussel, Budgets pro Schlussel, Fallbacks und fest angeheftete Images zentral verwalten.

Was ein selbst gehostetes LLM-Gateway leistet

LiteLLM ist ein Open-Source-LLM-Gateway, das Sie selbst hosten: ein HTTP-Endpunkt, den alle Ihre Anwendungen aufrufen. Anschließend leitet das Gateway jede Anfrage an den passenden Anbieter weiter. LLM steht für Large Language Model. Das Gateway verwendet die OpenAI Chat Completions API (Application Programming Interface). Daher funktioniert jede Client-Bibliothek, die bereits mit OpenAI kommuniziert, nach zwei Änderungen damit: der Basis-URL und dem Schlüssel.

Genau diese Indirektionsschicht ist der entscheidende Punkt. Ihre Anwendungen enthalten keine Zugangsdaten der Anbieter mehr. Ein Modell wechseln Sie mit einer Zeile in einer Konfigurationsdatei auf dem Server statt durch eine Codeänderung in fünf Diensten. Da jeder Aufruf durch einen Prozess läuft, haben Sie außerdem eine zentrale Stelle für ein Budget und für die Aufzeichnung der Ausgaben.

Nach der Einrichtung stehen Ihnen folgende Funktionen zur Verfügung:

  • Ein Endpunkt. Anwendungen verwenden https://gateway.example.com/v1 und fordern einen von Ihnen festgelegten Modellnamen an, beispielsweise bulk oder strong.
  • Virtuelle Schlüssel. Jede Anwendung erhält einen eigenen Schlüssel mit einer eigenen Liste zulässiger Modelle und einer eigenen Ausgabenobergrenze. Sie können einen Schlüssel widerrufen, ohne die anderen zu ändern.
  • Fallbacks. Ein fehlgeschlagener Aufruf oder ein zu großer Prompt wird automatisch mit einem anderen Modell wiederholt.
  • Ein protokollierter Datensatz. Jede Anfrage schreibt eine Zeile mit den entstandenen Kosten. Damit lässt sich feststellen, welche Anwendung die Ausgaben verursacht hat.

Warum Sie das Gateway selbst betreiben sollten

Ein verwalteter Router funktioniert nach demselben Prinzip, wobei bei jeder Anfrage der Prozess eines anderen Anbieters zwischengeschaltet ist. Wenn Sie das Gateway selbst betreiben, bleiben Ihre Provider-Schlüssel und Ihre Prompt-Texte auf einem System, das Sie kontrollieren. Der Aufwand ist real: Sie betreiben nun die Komponente, von der jede Anwendung abhängt. Der letzte Abschnitt dieses Leitfadens behandelt diesen Aufwand, weil er in den meisten Anleitungen ausgelassen wird.

Was Sie benötigen

  • Einen VPS (virtual private server) mit Ubuntu 24.04, auf dem Docker und das Compose-Plugin installiert sind.
  • Einen Domainnamen, der auf den VPS zeigt, wenn Rechner außerhalb des Servers das Gateway über TLS (transport layer security) erreichen.
  • Mindestens einen API-Schlüssel eines Providers.

Das Gateway führt keine Inferenz aus. Es leitet Anfragen weiter und streamt die Antworten zurück. Die CPU-Auslastung hängt daher vom Anfragevolumen und nicht von der Modellgröße ab. Ein System mit 1 vCPU kann problemlos einige interne Anwendungen bedienen. Das Wachstum betrifft die Datenbank, weil das Gateway für jede Anfrage einen Ausgabeposten erfasst.

Schreiben Sie zuerst config.yaml

Die Konfigurationsdatei legt fest, welche Modelle ein Client anfordern darf. Vier Abschnitte auf oberster Ebene sind relevant: model_list, litellm_settings, router_settings und general_settings.

model_list:
  - model_name: bulk
    litellm_params:
      model: anthropic/claude-haiku-4-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: anthropic/claude-sonnet-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: openai/gpt-5.5
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  num_retries: 2
  request_timeout: 120
  json_logs: true
  set_verbose: false

router_settings:
  allowed_fails: 3
  cooldown_time: 30
  fallbacks: [{"bulk": ["strong"]}]
  context_window_fallbacks: [{"bulk": ["strong"]}]

general_settings:
  background_health_checks: true
  health_check_interval: 300

model_name ist der Name, den Ihre Clients senden. litellm_params.model ist das tatsächliche Modell, angegeben als provider/model. Benennen Sie Ihre Modelle nach ihrer Aufgabe und nicht nach dem Anbieter. Eine Anwendung, die bulk anfordert, funktioniert weiterhin, wenn Sie nächsten Monat entscheiden, dass bulk ein anderes Modell sein soll.

api_key: os.environ/ANTHROPIC_API_KEY weist LiteLLM an, diese Variable zur Laufzeit zu lesen. Der tatsächliche Schlüssel erscheint nie in der Datei. Das ist wichtig, weil Sie config.yaml in das Repository übernehmen.

Zwei Einträge verwenden absichtlich den Namen strong. Wenn mehrere Deployments dasselbe model_name verwenden, behandelt der Router sie als austauschbar und verteilt Anfragen für diesen Namen auf sie. So bleibt strong verfügbar, wenn ein Anbieter vorübergehend Probleme hat: Sobald sich ein Deployment in der Abkühlphase befindet (siehe unten), werden neue Anfragen für strong an das andere Deployment gesendet.

num_retries: 2 wiederholt einen wiederholbaren Fehler bis zu zweimal. Bei diesen Wiederholungen kann ein anderes verfügbares Deployment in derselben Modellgruppe ausgewählt werden: Die LiteLLM-Architekturdokumentation, geprüft am 2026-10-29, beschreibt Wiederholungen innerhalb der Gruppe und Fallbacks zwischen Gruppen als getrennte Schritte. Eine Wiederholung für strong kann daher beim anderen Anbieter landen, ohne gewichtetes Failover zu aktivieren. Ein Fallback wird erst ausgelöst, nachdem diese Wiederholungen ausgeschöpft sind.

allowed_fails und cooldown_time sind Router-Optionen und stehen daher unter router_settings. Der Proxy erstellt seinen Router aus diesem Abschnitt. In v1.95.0 erreicht ein unter litellm_settings eingetragenes cooldown_time den Router nicht, sodass die Abkühlzeiten unbemerkt beim Standardwert von 5 Sekunden bleiben. cooldown_time: 30 legt fest, wie lange ein Deployment nach dem Beginn der Abkühlphase nicht berücksichtigt wird. Die Konfigurationsreferenz beschreibt allowed_fails als die Anzahl von Fehlern, die ein Deployment innerhalb von cooldown_time überschreiten muss, bevor es in die Abkühlphase wechselt. Im Quellcode von v1.95.0 ist allowed_fails: 3 der Standardwert. Bei diesem Standardwert verwendet der Router die integrierten Auslöser. Die Fehleranzahl wird erst verwendet, wenn Sie eine andere Zahl festlegen. Zu diesen Auslösern gehören eine sofortige Abkühlphase bei einer 429-Antwort für eine Gruppe mit mehr als einem Deployment, mehr als 50% Fehler im aktuellen Minutenintervall nach mindestens 5 Anfragen für eine solche Gruppe sowie Fehler, die der Router nicht wiederholt, etwa ein 401 aufgrund eines ungültigen Schlüssels. Ein separater Auslöser versetzt außerdem ein einzelnes Deployment in die Abkühlphase, wenn alle Anfragen im aktuellen Minutenintervall fehlschlagen und der Datenverkehr SINGLE_DEPLOYMENT_TRAFFIC_FAILURE_THRESHOLD erreicht. Dieser Wert ist standardmäßig auf 1000 Anfragen gesetzt. Ein Anbieter, der in kurzer Zeit 500-Fehler zurückgibt, wird nicht mehr bei jeder einzelnen Anfrage versucht. Ein einzelner 500-Fehler alle paar Minuten löst die Abkühlphase dagegen nicht aus.

fallbacks und context_window_fallbacks haben unterschiedliche Auslöser. Der zweite ist der nützliche, den viele überspringen.

  • fallbacks wird ausgelöst, wenn der primäre Aufruf fehlschlägt.
  • context_window_fallbacks wird ausgelöst, wenn der Anbieter die Anfrage ablehnt, weil sie länger als das Kontextfenster des jeweiligen Modells ist. Eine zu große Eingabeaufforderung wird dann an ein Modell mit ausreichendem Platz gesendet, statt dem Aufrufer einen Fehler zurückzugeben.

Außerdem gibt es content_policy_fallbacks für einen Anbieter, der eine Anfrage aufgrund von Inhaltsrichtlinien ablehnt. Setzen Sie diese Option nur, wenn Sie einen sinnvollen Zielort für solche Aufrufe haben.

LiteLLM mit Docker Compose auf einem VPS bereitstellen

Erstellen Sie ein Verzeichnis mit den drei Dateien config.yaml, docker-compose.yml und .env. Der Upstream-Schnellstart verwendet den Tag latest. Pinnen Sie stattdessen einen Release-Tag. Dann liefert docker compose up -d im nächsten Monat dasselbe Gateway wie heute. Ein Rollback besteht dann aus einer einzigen Zeile.

services:
  litellm:
    image: ghcr.io/berriai/litellm:v1.95.0
    restart: unless-stopped
    command: ["--config", "/app/config.yaml", "--num_workers", "1"]
    ports:
      - "127.0.0.1:4000:4000"
    volumes:
      - ./config.yaml:/app/config.yaml:ro
    env_file: .env
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: litellm
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: litellm
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U litellm"]
      interval: 5s
      timeout: 5s
      retries: 10
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Compose liest .env hier zweimal. Einmal, um ${POSTGRES_PASSWORD} direkt in der Compose-Datei zu ersetzen. Ein weiteres Mal über env_file, um jede Variable an den Container zu übergeben.

v1.95.0 war im August 2026 das aktuelle Release. Prüfen Sie die Release-Seite des Projekts und pinnen Sie beim Deployment die dann aktuelle Version. Für jedes Release wird eine Signatur veröffentlicht. Sie können das Image daher prüfen, bevor Sie ihm vertrauen:

cosign verify --key https://raw.githubusercontent.com/BerriAI/litellm/v1.95.0/cosign.pub ghcr.io/berriai/litellm:v1.95.0

Die Port-Zeile lautet 127.0.0.1:4000:4000. Dadurch wird der Port nur auf der Loopback-Schnittstelle veröffentlicht. Schreiben Sie stattdessen 4000:4000, ist Ihr Gateway aus dem gesamten Internet erreichbar. Docker fügt eigene Regeln in die FORWARD-Kette von iptables ein. Diese werden vor den Regeln von ufw ausgewertet. Daher verhindert ufw deny 4000 den Zugriff nicht. Auf diese Weise wird ein selbst gehostetes Gateway am häufigsten versehentlich öffentlich erreichbar: siehe wie Docker einen Container-Port direkt an ufw vorbei veröffentlicht. Externer Datenverkehr kommt stattdessen über den Reverse Proxy an.

Provider-Schlüssel aus dem Image heraushalten

Die Datei .env enthält alle Secrets. Sie wird zur Laufzeit als Umgebung übergeben. Dadurch wird sie niemals in das Image eingebaut und niemals in das Repository committed.

LITELLM_MASTER_KEY=sk-REPLACE_ME
LITELLM_SALT_KEY=sk-REPLACE_ME_TOO
POSTGRES_PASSWORD=REPLACE_ME_AS_WELL
DATABASE_URL=postgresql://litellm:REPLACE_ME_AS_WELL@db:5432/litellm
STORE_MODEL_IN_DB=True
LITELLM_MODE=PRODUCTION
LITELLM_LOG=ERROR
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-proj-...

Erzeugen Sie die beiden LiteLLM-Schlüssel mit echter Zufallsquelle. Schränken Sie anschließend die Dateiberechtigungen ein:

printf 'sk-%s\n' "$(openssl rand -hex 32)"
chmod 600 .env

LITELLM_MASTER_KEY ist das Administratorkennwort. Es authentifiziert die Management-API und ist das Kennwort für die Admin UI unter /ui. Keine Anwendung sollte diesen Schlüssel jemals enthalten.

LITELLM_SALT_KEY verschlüsselt die in der Datenbank gespeicherten Zugangsdaten der Provider. Setzen Sie den Wert einmal und ändern Sie ihn danach nicht mehr. Wenn Sie ihn später ändern, können die bereits gespeicherten Zugangsdaten nicht mehr entschlüsselt werden. Das Gateway startet dann zwar normal, aber jeder Aufruf dieser Provider schlägt bei der Authentifizierung fehl.

Mit STORE_MODEL_IN_DB=True können Sie Modelle über die Admin UI hinzufügen und bearbeiten, ohne config.yaml anzupassen. Das ist praktisch, teilt jedoch die Quelle der Wahrheit in zwei Teile. Legen Sie fest, welche Quelle maßgeblich ist, und dokumentieren Sie diese Entscheidung neben der Konfiguration.

Der Grund, Provider-Schlüssel aus der Konfigurationsdatei herauszuhalten, gilt auch für die Werkzeuge, die Sie einem Agenten übergeben. Provider-Secrets aus KI-Agenten heraushalten beschreibt dieses Muster. env-Dateien und Secrets in Docker Compose erläutert die technische Umsetzung.

Starten Sie den Stack und überwachen Sie den ersten Start:

docker compose up -d
docker compose logs -f litellm

Prüfen, ob der Dienst tatsächlich funktioniert

Es gibt zwei nicht authentifizierte Prüfungen und eine authentifizierte Prüfung. Sie schlagen aus unterschiedlichen Gründen fehl.

curl -s http://127.0.0.1:4000/health/liveliness
curl -s http://127.0.0.1:4000/health/readiness

/health/liveliness benötigt keine Authentifizierung und antwortet mit "I'm alive!", solange der Prozess läuft. Auch /health/readiness benötigt keine Authentifizierung. Die Prüfung gibt ein JSON-Objekt mit "status": "healthy" und einem Feld db zurück oder 503, wenn die Datenbank nicht erreichbar ist. Richten Sie Ihr Monitoring auf die Readiness-Prüfung. Die Liveness-Prüfung bleibt auf einem Gateway grün, das keinen einzigen virtuellen Schlüssel auflösen kann.

Die authentifizierte Prüfung kommuniziert mit den Providern:

curl -s http://127.0.0.1:4000/health \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

Sie antwortet mit den Arrays healthy_endpoints und unhealthy_endpoints. Ein Modell in unhealthy_endpoints mit einem Authentifizierungsfehler weist darauf hin, dass der Provider-Schlüssel in .env falsch ist oder fehlt. Das ist der Fehler, den Sie jetzt finden möchten. Da background_health_checks: true gesetzt ist, führt der Proxy diese Prüfungen alle health_check_interval Sekunden selbstständig aus, und /health gibt das letzte Ergebnis zurück. Das Abrufen dieses Endpunkts sendet daher nicht jedes Mal eine Testanfrage an Ihre Provider.

Virtuelle Schlüssel und Budgets pro Schlüssel

Jede Anwendung erhält einen eigenen Schlüssel, der mit dem Hauptschlüssel erstellt wird.

curl -s http://127.0.0.1:4000/key/generate \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "key_alias": "nightly-summariser",
    "models": ["bulk"],
    "max_budget": 5,
    "budget_duration": "30d",
    "rpm_limit": 60,
    "tpm_limit": 200000
  }'

Die Antwort enthält ein Feld key, das mit sk- beginnt. Dieser String wird an die Anwendung übergeben. Die Anwendung erhält niemals etwas anderes.

  • models ist eine Allowlist für die Anforderungen, die dieser Schlüssel stellen darf. Der oben gezeigte Schlüssel kann bulk anfordern und nichts anderes.
  • max_budget: 5 mit budget_duration: "30d" beträgt fünf US-Dollar pro Kalendermonat. LiteLLM setzt ein 30d-Budget am 1. jedes Monats um Mitternacht zurück, sofern Sie unter litellm_settings nicht timezone festlegen. Der Zeitraum umfasst also keine gleitenden 30 Tage. Sobald der Schlüssel fünf US-Dollar verbraucht hat, funktioniert er bis zum nächsten 1. nicht mehr.
  • rpm_limit und tpm_limit begrenzen die Anfragen pro Minute und die Tokens pro Minute ausschließlich für diesen Schlüssel.
  • key_alias ist die Bezeichnung, die Sie sechs Wochen später im Ausgabenprotokoll wiedererkennen. Setzen Sie sie immer.

Wenn das Budget aufgebraucht ist, schlägt der Aufruf mit einem Fehler vom Typ budget_exceeded und einer Meldung dieser Form fehl:

Budget has been exceeded! Key=<key> Current cost: <spend>, Max budget: <max_budget>

Der Statuscode hängt vom Release ab. Im festgelegten Release v1.95.0 lautet er HTTP 429. Ab v1.104.0 ist HTTP 422 der Standard. Wenn Sie unter litellm_settings budget_exceeded_status_code: 429 setzen, wird der frühere Code wiederhergestellt. Echte Treffer von rpm_limit und tpm_limit liefern in beiden Releases weiterhin 429 zurück. Prüfen Sie nach jedem Upgrade, welchen Code Sie erhalten.

Der Statuscode macht dies verwirrend, weil keiner der beiden Codes auf ein Budget hinweist. Eine Clientbibliothek meldet 429 als Rate-Limit-Fehler und wiederholt die Anfrage möglicherweise mit Backoff, obwohl ein aufgebrauchtes Budget erst beim Zurücksetzen wieder verfügbar ist. 422 meldet sie als Anfrage, die nicht verarbeitet werden konnte. Protokollieren Sie den Antworttext zusammen mit dem Statuscode. Andernfalls sieht die Budgeterschöpfung jedes Mal wie ein anderer Fehler aus.

Sie können Schlüssel über dieselbe Management-API anzeigen und anpassen:

curl -s "http://127.0.0.1:4000/key/info?key=sk-..." \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

curl -s -X POST http://127.0.0.1:4000/key/update \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"key": "sk-...", "max_budget": 25}'

Ein am Gateway durchgesetztes Budget gilt auch dann, wenn das Problem beim Agenten selbst liegt. Deshalb bildet es das Rückgrat der Kostenkontrolle für KI-Agenten auf einem VPS.

Massenverarbeitung an ein günstiges Modell senden

Richten Sie einen Client auf das Gateway. Basis-URL, Schlüssel und Modellname:

curl -s http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "bulk",
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'

Jede OpenAI-Clientbibliothek funktioniert auf dieselbe Weise: Setzen Sie base_url auf https://gateway.example.com/v1 und api_key auf den virtuellen Schlüssel.

Die Routing-Richtlinie aus config.yaml wird nun angewendet, ohne dass der Aufrufer davon wissen muss. Eine Anfrage für bulk wird an das günstige Modell gesendet. Wenn dieser Aufruf nach seinen Wiederholungsversuchen fehlschlägt, wird die Anfrage erneut an strong gesendet. Wenn die Eingabeaufforderung für bulk zu lang ist, sendet context_window_fallbacks sie stattdessen an strong, anstatt einen Fehler zurückzugeben. Massenverarbeitung wie ein Klassifizierungslauf oder die Zusammenfassung eines Rückstands wird standardmäßig günstig ausgeführt. Nur anspruchsvolle Anfragen verursachen höhere Kosten.

Hier zeigt sich auch der Nutzen eines Gateways für Agenten, die Tools verwenden. Ein MCP-Server (Model Context Protocol) auf demselben VPS und der ihn steuernde Agent können beide auf einen einzigen Endpunkt zeigen. Dadurch kann das dahinterliegende Modell gewechselt werden, ohne einen der beiden Dienste erneut bereitzustellen.

Wie erkennen Sie, dass ein Fallback stattgefunden hat?

Dieser Fehlerfall kann Kosten verursachen, weil nichts offensichtlich fehlschlägt. Ein erfolgreicher Fallback liefert HTTP 200 mit einem normalen Antworttext. Ihr günstiges Modell kann einen Tag lang ausgefallen sein, während jede Anfrage unbemerkt vom teureren Modell verarbeitet wird. Der erste Hinweis ist dann die Rechnung.

Die Informationen sind in den Response-Headern enthalten. Fordern Sie sie an:

curl -s -D - -o /dev/null http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{"model":"bulk","messages":[{"role":"user","content":"ping"}]}' \
  | grep -i '^x-litellm'
  • x-litellm-model-group ist der Modellname, unter dem die Anfrage geroutet wurde, beispielsweise bulk. x-litellm-model-id ist die ID des Deployments, das geantwortet hat. Diese ID können Sie mit /v1/model/info?litellm_model_id=... nachschlagen. Wenn die ID zu keinem Ihrer bulk-Deployments gehört, hat ein Fallback stattgefunden.
  • x-litellm-attempted-fallbacks und x-litellm-attempted-retries zählen diese Fälle. Bei einer fehlerfreien Anfrage ist keiner der beiden Werte größer als 0.
  • x-litellm-response-cost sind die Kosten dieses einzelnen Aufrufs in US-Dollar.
  • x-litellm-call-id ist die Kennung, mit der Sie denselben Aufruf in Ihren Logs finden.

Protokollieren Sie x-litellm-attempted-fallbacks bei jeder Anfrage und lösen Sie einen Alarm aus, sobald der Wert größer als 0 ist. Diese eine Zahl entscheidet darüber, ob Ihre Routing-Richtlinie funktioniert oder stillschweigend zu „immer das teurere Modell verwenden“ geworden ist.

Die vollständige Lösung dafür ist Tracing und verdient eine eigene Einrichtung: Self-Hosted Langfuse für das Tracing von Agent-Aufrufen. LiteLLM liefert den Callback mit. Die Einrichtung umfasst daher zwei Zeilen plus Zugangsdaten.

litellm_settings:
  success_callback: ["langfuse"]
  failure_callback: ["langfuse"]
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://langfuse.example.com

Für Langfuse v3 und spätere Versionen empfiehlt die Langfuse-Seite von LiteLLM jetzt das Preset langfuse_otel. Der oben gezeigte Callback langfuse ist weiterhin dokumentiert. Setzen Sie sowohl failure_callback als auch success_callback. Wenn Sie darauf verzichten, bleiben nur die Traces erhalten, bei denen nichts schiefgelaufen ist. Unabhängig davon schreibt LiteLLM für jede Anfrage eine Ausgabenzeile in Postgres. Die Admin UI unter /ui liest diese Tabelle. Die Tabelle wächst mit dem Traffic. Überwachen Sie sie daher auf einem kleinen Datenträger.

Gateway hinter einem Reverse Proxy betreiben

Nichts außerhalb des Servers sollte Port 4000 erreichen. Beenden Sie TLS in nginx oder Caddy und leiten Sie den Datenverkehr an die Loopback-Adresse weiter.

location / {
    proxy_pass http://127.0.0.1:4000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_read_timeout 600s;
}

Zwei dieser Zeilen werden häufig weggelassen. proxy_buffering off ist wichtig, weil eine gestreamte Completion aus einer Reihe von Server-Sent Events besteht. Wenn das Buffering in nginx aktiviert ist, sammelt nginx die Datenblöcke zunächst in Puffern, bevor sie weitergeleitet werden. Dadurch verzögert sich die Zustellung an den Client. proxy_read_timeout 600s ist wichtig, weil der nginx-Standardwert von 60 Sekunden die längste Wartezeit zwischen zwei Lesevorgängen vom Gateway angibt und keine Begrenzung für die gesamte Antwort ist. Eine nicht gestreamte Completion sendet erst Daten, wenn die Generierung abgeschlossen ist. Eine lange Completion überschreitet daher 60 Sekunden, ohne dass ein Byte eintrifft. Der Client erhält dann einen 504-Fehler, während das Error-Log upstream timed out (110: Connection timed out) while reading response header from upstream protokolliert. Eine gestreamte Completion, die weiterhin Datenblöcke sendet, kann deutlich länger als 60 Sekunden laufen. Der Timeout wird nur ausgelöst, wenn das Modell so lange keine Daten sendet.

Für das Zertifikat ist Certbot mit Let's Encrypt unter nginx der kürzeste Weg. Wenn der Server bereits mehrere Container bereitstellt, übernimmt Traefik vor mehreren Compose-Anwendungen Routing und Zertifikate zentral.

Das Gateway ist jetzt ein Single Point of Failure

Seien Sie ehrlich, was Sie aufgebaut haben. Jede Ihrer Anwendungen hängt jetzt von einem Container auf einem VPS ab. Solange dieser nicht verfügbar ist, kann nichts ein Modell aufrufen, auch keine Anbieter, die selbst ordnungsgemäß funktionieren. Daraus folgen vier Punkte.

  • Eine fehlerhafte Konfiguration legt alles gleichzeitig lahm. restart: unless-stopped startet einen abgestürzten Container neu und startet auch einen Container immer wieder, der config.yaml nicht parsen kann. Lesen Sie docker compose logs litellm nach jeder Konfigurationsänderung, und nehmen Sie Änderungen vor, wenn Sie Zeit haben, sie zu überwachen.
  • Postgres liegt bei einem Cache-Miss im Anfragepfad. Die Suche nach dem virtuellen Schlüssel verwendet Postgres, wenn der Schlüssel nicht im Cache liegt; die Aufzeichnung der Nutzungskosten läuft nach der Antwort asynchron. Dass /health/readiness 503 zurückgibt, ist Ihr Warnsignal, dass das Gateway zwar läuft, aber keines von beidem ausführen kann.
  • Nutzen Sie zuerst die CPU-Kerne, und fügen Sie danach Instanzen hinzu. Die Produktionsvorgabe des Projekts von einem Worker pro Instanz (--num_workers 1) gilt für Kubernetes oder einen anderen Pod-Autoscaler, der automatisch Instanzen hinzufügt. Für eine einzelne VM oder einen einzelnen Bare-Container wie diesen bedeutet sie das Gegenteil: Setzen Sie die Anzahl der Worker auf die vCPU-Anzahl des Rechners. Auf dem oben genannten Rechner mit 1 vCPU sind das weiterhin --num_workers 1; auf einem größeren Rechner erhöhen Sie den Wert, und bedenken Sie, dass jeder Worker seinen eigenen Speicher benötigt. Ein zweites Gateway hinter einem Load Balancer entfernt den einzelnen Container aus dieser Abhängigkeit. Die Datenbank bleibt jedoch weiterhin ein zentraler Bestandteil.
  • Sichern Sie, was Sie nicht neu erzeugen können. Das sind config.yaml und .env sowie ein pg_dump der Datenbank. Der Verlust von LITELLM_SALT_KEY macht die darin enthaltenen verschlüsselten Zugangsdaten der Anbieter unbrauchbar. Deshalb gehören die Env-Datei und der Dump in denselben Backup-Job: restic-Backups auf externen Speicher.

Für ein Upgrade ändern Sie den Image-Tag und führen docker compose up -d aus. LiteLLM führt beim Start standardmäßig prisma migrate deploy aus. Der neue Container migriert daher das Datenbankschema beim ersten Start. Erstellen Sie den Dump, bevor Sie den Tag ändern, denn das Zurücksetzen auf das alte Image macht eine bereits ausgeführte Migration nicht rückgängig.

Lesen Sie die Release Notes jeder übersprungenen Version, nicht nur der Version, auf der Sie landen. Der Wechsel von v1.95.0 auf v1.104.0 oder höher umfasst mehrere inkompatible Änderungen: den oben genannten Budget-Statuscode, ein Proxy, der mit einem nicht gesetzten, leeren oder öffentlich bekannten Master Key nicht startet, sowie ein Proxy, der beendet wird, wenn die Datenbankeinrichtung beim Start fehlschlägt. Unter restart: unless-stopped erscheint dies als Neustartschleife. Wenn Sie von v1.102.x oder früher kommen, fordern die Notes zu v1.104.0 außerdem dazu auf, zwei LiteLLM_SpendLogs-Indizes selbst zu erstellen.

FAQ

Fügt LiteLLM jedem Aufruf eine spürbare Latenz hinzu?

Das Projekt gibt im README für Oktober 2026 eine Latenz von 8 ms im 95. Perzentil bei 1000 Anfragen pro Sekunde an. Behandeln Sie diesen Wert als Herstellerangabe. Die tatsächliche Latenz wird durch die Netzwerkentfernung zwischen Ihren Anwendungen und dem Gateway bestimmt, weil jeder Aufruf um einen Round Trip erweitert wird. Betreiben Sie das Gateway in derselben Region wie die Anwendungen, die es aufrufen, und messen Sie anschließend Ihren eigenen Overhead mit dem Header x-litellm-overhead-duration-ms in einer echten Antwort.

Warum funktioniert Streaming nicht mehr, nachdem ich nginx vorgeschaltet habe?

Weil nginx Antworten vom Upstream standardmäßig puffert und eine gestreamte Antwort aus einer Reihe von Server-Sent Events besteht. Wenn proxy_buffering aktiviert ist, sammelt nginx die Datenblöcke in Puffern, bevor es sie weiterleitet. Dadurch verzögert sich die Zustellung an den Client. Setzen Sie proxy_buffering off; im location-Block. Erhöhen Sie im selben Block außerdem proxy_read_timeout. Standardmäßig bricht nginx nach 60 Sekunden ohne Daten vom Gateway ab. Eine lange nicht gestreamte Generierung erreicht diesen Wert problemlos, und der Client erhält einen 504-Fehler.

Was passiert, wenn das Budget eines virtuellen Schlüssels aufgebraucht ist?

Der Aufruf schlägt mit einem Fehler vom Typ budget_exceeded und einer Meldung der Form Budget has been exceeded! Key=<key> Current cost: <spend>, Max budget: <max_budget> fehl. In der festgelegten Version v1.95.0 lautet der Status HTTP 429; ab v1.104.0 ist standardmäßig HTTP 422 eingestellt. Keiner der beiden Codes weist auf das Budget hin. Das ist die Falle: Eine Client-Bibliothek meldet 429 als Rate-Limit-Fehler und wiederholt den Aufruf möglicherweise. Dadurch suchen Sie nach einer Drosselung, statt die Meldung zu lesen. Protokollieren Sie den Antworttext zusammen mit dem Statuscode. Prüfen Sie die tatsächliche Position des Schlüssels mit /key/info?key=sk-... gegen den Master-Schlüssel. Erhöhen Sie die Obergrenze mit /key/update, wenn das Budget zu niedrig festgelegt wurde.

Kann das Gateway neben gehosteten Modellen auch ein lokales Modell ansteuern?

Ja. Es ist ein weiterer Eintrag in model_list. Verwenden Sie das Präfix ollama_chat/ zusammen mit einem api_base, zum Beispiel model: ollama_chat/llama3.1 neben api_base: http://ollama:11434. Im standardmäßigen Compose-Netzwerk bezeichnet localhost innerhalb eines Containers diesen Container selbst. Verwenden Sie daher den Namen des Compose-Dienstes oder die Adresse des Hosts im Docker-Netzwerk, niemals 127.0.0.1. Eine Ausnahme ist das Host-Netzwerk, bei dem der Container den Netzwerk-Stack des Hosts gemeinsam verwendet. Das lokale Modell bereitzustellen ist eine separate Aufgabe: Siehe einen LLM mit Ollama auf einem VPS selbst hosten.

War dieser Artikel hilfreich?