SSD Nodes Learn Hosting plans →
Anleitungen Matt ConnorVon Matt Connor

Langfuse für KI-Agenten selbst hosten: VPS-Setup

Betreiben Sie Langfuse auf dem eigenen VPS: mit realer Ressourcenuntergrenze, festen Image-Tags, TLS, ClickHouse-Aufbewahrung und funktionierenden Backups.

Warum einen KI-Agenten überhaupt nachverfolgen?

Sie hosten Langfuse selbst, um zu sehen, was Ihr Agent bei einer Ausführung tatsächlich getan hat. Langfuse ist ein Open-Source-Tool zur Beobachtung von LLMs (Large Language Models). Es zeichnet jeden Prompt, jede Modellantwort, jeden Tool-Aufruf und jedes Token auf und fasst sie anschließend unter einem Trace zusammen, den Sie öffnen und auswerten können. Wenn Sie Langfuse auf Ihrem eigenen VPS betreiben, verlassen diese Prompts niemals einen Server, den Sie kontrollieren.

Der Grund dafür ist einfach. Ein Kosten- oder Qualitätsproblem lässt sich nicht beheben, wenn es nicht sichtbar ist. Eine Anbieterrechnung zeigt Ihnen, dass Dienstag viermal so viel gekostet hat wie Montag. Ein Trace zeigt, welcher Agent-Lauf dafür verantwortlich war, welcher Prompt auf 40,000 Tokens angewachsen ist und welche Retry-Schleife neunmal ausgeführt wurde, bevor sie abgebrochen hat. Die Rechnung liefert die Zahl. Der Trace zeigt den Code, der sie verursacht hat.

In diesem Leitfaden werden drei Begriffe verwendet. Ein Trace ist ein vollständiger Lauf Ihres Agenten von Anfang bis Ende. Eine Observation ist ein einzelner Schritt innerhalb dieses Laufs: ein Span für gewöhnlichen Code oder eine Generation für den Aufruf eines Modells. Ein Score ist eine Zahl, die einem Trace zugeordnet wird und aus einer menschlichen Bewertung oder einer automatisierten Evaluierung stammt. Langfuse verwendet OpenTelemetry (OTel), den herstellerunabhängigen Standard für Distributed Tracing. Daher kann eine bereits vorhandene Instrumentierung auf Langfuse verweisen.

Was Self-Hosting von Langfuse tatsächlich ausführt

Langfuse v4 besteht nicht aus einem Container. Es sind zwei Anwendungscontainer und vier Speicherdienste. Auf einem einzelnen VPS laufen alle sechs auf Ihrem System.

  • langfuse-web stellt die Weboberfläche und die Ingestion-API bereit.
  • langfuse-worker leert die Warteschlange im Hintergrund. Der Container verarbeitet Ingestion-Batches, berechnet Kosten und führt den nächtlichen Aufbewahrungsjob aus.
  • Postgres speichert Transaktionsdaten wie Benutzer, Organisationen, Projekte, API-Keys und Prompts.
  • ClickHouse speichert die Trace-Daten selbst, also Observations und Scores. Es ist ein spaltenorientierter Speicher für analytische Abfragen. Deshalb antwortet ein Dashboard über mehr als hundert Millionen Zeilen weiterhin schnell.
  • Redis ist die Warteschlange und der Cache zwischen Webcontainer und Worker.
  • MinIO stellt S3-kompatiblen Objektspeicher auf dem System bereit. Es speichert jedes eingehende Rohereignis sowie alle angehängten Medien.

Langfuse veröffentlicht Mindestressourcen für jede ausgeführte Komponente. Das Diagramm zeigt die drei Komponenten, die die eigentliche Arbeit ausführen.

DiagrammLangfuse published minimum resources per component
Die Daten hinter diesem Diagramm
[
  {
    "label": "ClickHouse",
    "cpu_cores": 2,
    "memory_gib": 8
  },
  {
    "label": "Langfuse web",
    "cpu_cores": 2,
    "memory_gib": 4
  },
  {
    "label": "Langfuse worker",
    "cpu_cores": 2,
    "memory_gib": 4
  }
]

ClickHouse benötigt allein 8 GiB Arbeitsspeicher. Der Webcontainer und der Worker benötigen jeweils 4 GiB. Das sind die veröffentlichten Mindestwerte für die 3 Komponenten im Diagramm. Auf derselben Seite werden für Postgres 4 GiB, für Redis oder Valkey 1.5 GiB und für MinIO 4 GiB genannt. Damit ergeben sich für alle sechs Komponenten mindestens 25.5 GiB. Die Docker-Compose-Anleitung des Projekts empfiehlt für eine einzelne Maschine mindestens 4 Kerne, 16 GiB Arbeitsspeicher und etwa 100 GiB Speicherplatz. Das liegt unter der Summe der Mindestwerte der einzelnen Komponenten. Betrachten Sie 16 GiB daher als das absolute Minimum für ein gemeinsam genutztes Compose-System und nicht als Reserve.

Versuchen Sie das nicht mit einem 2-GiB-Tarif. ClickHouse startet und nimmt eine Zeit lang Schreibvorgänge an. Anschließend beendet es sich während eines Hintergrund-Merges, weil ein Merge große Tabellenteile in den Arbeitsspeicher lädt. Sie sehen, wie docker compose ps den clickhouse-Container als restarting meldet, dmesg eine Zeile wie Out of memory: Killed process 1234 (clickhouse-serv) ausgibt und jedes Langfuse-Dashboard mit HTTP-Status 500 antwortet. Bei geringerer Last verweigert ClickHouse stattdessen die Abfrage und schreibt DB::Exception: Memory limit (total) exceeded in das Log. Planen Sie mit 16 GiB. Wenn derselbe VPS noch andere Dienste ausführen soll, müssen Sie deren Ressourcen zusätzlich einplanen. Selbst ein vergleichsweise schlanker Stack wie ein selbst gehosteter AFFiNE-Arbeitsbereich benötigt eigene Gigabytes. ClickHouse gibt davon keinen Arbeitsspeicher zurück.

Langfuse mit Docker Compose bereitstellen

Klonen Sie das Repository. Der Stack, die Verbindungen und die Standardumgebung befinden sich in dessen docker-compose.yml.

git clone https://github.com/langfuse/langfuse.git
cd langfuse

Jeder Wert, den Sie ändern müssen, ist in dieser Datei mit # CHANGEME markiert. Generieren Sie zuerst die drei Anwendungsgeheimnisse.

openssl rand -base64 32   # NEXTAUTH_SECRET
openssl rand -base64 32   # SALT
openssl rand -hex 32      # ENCRYPTION_KEY

ENCRYPTION_KEY muss 256 Bit umfassen und als 64 Hexadezimalzeichen geschrieben sein. Genau das gibt openssl rand -hex 32 aus. Damit werden vertrauliche Werte im Ruhezustand verschlüsselt, darunter alle LLM-Provider-Schlüssel, die Sie in der Instanz speichern. Wenn Sie den Wert ändern, nachdem bereits Daten vorhanden sind, können diese Datensätze nicht mehr entschlüsselt werden. Behandeln Sie ihn daher ab dem ersten Start als dauerhaft. SALT wird zum Hashen Ihrer Langfuse-API-Schlüssel verwendet. Wenn Sie den Wert ändern, werden alle Schlüssel ungültig, die Ihre Agents bereits verwenden.

Setzen Sie anschließend POSTGRES_PASSWORD, CLICKHOUSE_PASSWORD, REDIS_AUTH und MINIO_ROOT_PASSWORD. Das MinIO-Passwort erscheint an vier Stellen: einmal als MINIO_ROOT_PASSWORD und anschließend als LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY, LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY und LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY. Wenn eine Stelle fehlt, weist MinIO den betreffenden Client mit SignatureDoesNotMatch zurück. Die Meldung erscheint im Worker-Log, während die Weboberfläche weiterhin fehlerfrei aussieht. Wenn Sie diese Werte in einer Env-Datei statt in der versionierten Compose-Datei speichern, entspricht das dem Muster aus Docker-Compose-Env-Dateien und Secrets.

Image-Tags vor dem Start festlegen

Die mitgelieferte Datei verwendet docker.langfuse.com/langfuse/langfuse:4 und docker.langfuse.com/langfuse/langfuse-worker:4. Diese Tags ändern sich. Langfuse führt seine Postgres- und ClickHouse-Migrationen beim Start automatisch aus. Dadurch wird aus einem routinemäßigen docker compose pull Monate später eine ungeplante Schema-Migration auf einer Datenbank, die Sie an diesem Morgen nicht gesichert haben. Fixieren Sie beide Images in einer docker-compose.override.yml auf dieselbe Version. Compose führt diese Datei zusätzlich zur mitgelieferten Datei zusammen. Ein späteres git pull überschreibt dann nicht versehentlich Ihre Änderungen. Die Images werden auch auf Docker Hub veröffentlicht. Von dort verwendet die folgende Override-Datei sie.

services:
  langfuse-web:
    image: docker.io/langfuse/langfuse:4.72.0
  langfuse-worker:
    image: docker.io/langfuse/langfuse-worker:4.72.0

Version 4.3.1 war im August 2026 die aktuelle 4.3-Version. Seitdem sind schnell weitere Releases erschienen. Am 11. Oktober 2026 war v4.56.0 die aktuelle Version auf GitHub. Das obige Override verwendet v4.72.0, die am 31. Oktober 2026 aktuelle Version auf GitHub. Prüfen Sie die GitHub-Releases-Seite des Projekts. Fixieren Sie die Version, die am Tag Ihrer Bereitstellung aktuell ist, und aktualisieren Sie diese Zahl später bewusst. Einige Storage-Images in der mitgelieferten Datei sollten ebenfalls auf diese Weise behandelt werden, weil sich auch ihre Tags ändern. postgres:17 (der Standardwert einer POSTGRES_VERSION-Variablen) und redis:7 folgen einer Hauptversion. Die mitgelieferte Datei verwendet inzwischen für docker.io/clickhouse/clickhouse-server ein exaktes Version-Tag und fixiert damit eine exakte ClickHouse-Version. Diese Regel gilt nicht nur für Langfuse: ein selbst gehosteter openGym-Workout-Tracker verwendet nur einen kleinen Teil dieses Stacks und benötigt trotzdem ein benanntes Git-Tag. Jede Anwendung, die ihre eigene Datenbank beim Start migriert, kann aus einem routinemäßigen Pull eine Schemaänderung machen.

Starten Sie den Stack.

docker compose up -d
docker compose ps
docker compose logs -f langfuse-worker

Beim ersten Start werden die Migrationen ausgeführt. Warten Sie daher zwei oder drei Minuten, bevor ein Dienst antwortet. Der Web-Container protokolliert Ready, sobald er bereit ist. docker compose ps sollte sechs Dienste mit dem Status running auflisten. Wenn ein Container in einer Neustartschleife läuft, enthält sein Log den Grund. ClickHouse-Migrationen werden beim Start des Web-Containers angewendet. Ein falsches CLICKHOUSE_MIGRATION_URL erscheint daher in docker compose logs langfuse-web und nicht im Worker-Log, das Sie gerade überwachen. Diese Variable verwendet das native ClickHouse-Protokoll auf Port 9000 und nicht den HTTP-Port 8123. Wenn Sie sie auf 8123 verweisen lassen, schlagen die Startmigrationen des Web-Containers fehl. Der Worker führt die langsameren Hintergrundmigrationen aus. Die entsprechenden Meldungen finden Sie daher in seinem Log.

Prüfen Sie den Zustand direkt auf dem Server.

curl -s "http://localhost:3000/api/public/health?failIfDatabaseUnavailable=true"
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/api/public/ready

Ein einfacher Aufruf von /api/public/health bestätigt nur, dass der API-Prozess läuft. Standardmäßig wird die Datenbank dabei nicht geprüft. Die Form failIfDatabaseUnavailable=true eignet sich für die Überwachung. Sie gibt 503 zurück, wenn die Datenbank nicht erreichbar ist. /api/public/ready gibt 200 zurück, wenn der Web-Container bereit ist, Datenverkehr zu empfangen, und 500, sobald ihm das Herunterfahren mitgeteilt wurde. Beide Prüfungen verwenden gewöhnliches HTTP. Eine Uptime-Kuma-Statusseite kann sie daher überwachen und Sie über einen Ausfall des Stacks informieren, bevor Ihre Agents ihn bemerken.

TLS davor schalten und die zusätzlichen Ports schließen

Die mitgelieferte Compose-Datei veröffentlicht 3000:3000 für den Web-Container und 9090:9000 für MinIO. Beide binden an alle Schnittstellen. Bei einer öffentlichen IP-Adresse bedeutet das: Jeder, der Port 3000 scannt, erreicht Ihre Registrierungsseite. Jeder, der Port 9090 scannt, greift auf den Bucket mit Ihren rohen Prompts zu.

Eine Firewall-Regel allein schließt diese Ports nicht. Docker schreibt eigene DNAT-Regeln in die nat-Tabelle. Diese werden ausgewertet, bevor die Filterregeln von ufw das Paket sehen. Daher lässt ufw deny 3000 den veröffentlichten Port weiterhin offen. Dieses Problem tritt so häufig auf, dass es dafür einen eigenen Leitfaden gibt: warum von Docker veröffentlichte Ports ufw umgehen. Binden Sie die Ports stattdessen in Ihrer Override-Datei an die Loopback-Schnittstelle. Verwenden Sie !override mit Docker Compose 2.24.4 oder höher, um die veröffentlichten Ports zu ersetzen. Eine einfache Liste wird mit den ursprünglichen Bindings zusammengeführt.

services:
  langfuse-web:
    ports: !override
      - "127.0.0.1:3000:3000"
    environment:
      NEXTAUTH_URL: https://langfuse.example.com
  minio:
    ports: !override
      - "127.0.0.1:9090:9000"
      - "127.0.0.1:9091:9001"

NEXTAUTH_URL muss die exakte öffentliche Adresse einschließlich Schema sein, weil der Anmeldeablauf daraus die Callback-URL erstellt. Lassen Sie den Wert hinter einem HTTPS-Proxy auf http://localhost:3000 stehen, leitet der Anmeldevorgang den Browser an eine Adresse weiter, die er nicht erreichen kann.

Richten Sie jetzt einen Reverse Proxy auf 127.0.0.1:3000 und lassen Sie ihn das Zertifikat verwalten. Für Traefik im selben Compose-Projekt leiten Sie Anfragen über das Compose-Netzwerk an langfuse-web:3000 weiter. Verwenden Sie dazu die Labels aus mehrere Anwendungen hinter einem Traefik-Reverse-Proxy betreiben. Caddy erledigt dieselbe Aufgabe mit zwei Zeilen, wenn Langfuse der einzige Dienst auf dem Server ist. Prüfen Sie die Konfiguration mit curl -sI https://langfuse.example.com/api/public/ready. Bestätigen Sie anschließend von einem zweiten Rechner aus, dass curl http://YOUR_IP:3000 keine Verbindung herstellen kann.

Bei MinIO gibt es eine Einschränkung. Langfuse gibt presigned URLs aus, die auf diesen S3-Endpunkt zeigen. Das gilt sowohl für SDKs, die Medien hochladen, als auch für Ihren Browser, der Medien lädt. Wenn Sie also multimodale Traces mit Bildern oder Audiodateien verwenden, kann ein nur an Loopback gebundenes MinIO keine Anhänge von anderen Rechnern entgegennehmen. Ihr Browser kann diese Anhänge ebenfalls nicht laden. Lesen Sie vor dem Proxying die Konfigurationsseite zum Blob-Speicher. Der in der presigned URL eingetragene Endpunkt LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT muss mit dem veröffentlichten Endpunkt übereinstimmen. Text-Traces sind davon nicht betroffen.

Erstellen Sie Ihr Konto beim ersten Besuch. Sichern Sie die Instanz anschließend ab. Setzen Sie AUTH_DISABLE_SIGNUP=true, sobald Ihr Konto existiert. Dann kann sich niemand, der die Seite erreicht, registrieren. Langfuse bietet außerdem LANGFUSE_ALLOWED_ORGANIZATION_CREATORS. Damit lässt sich festlegen, wer Organisationen erstellen darf. Diese Funktion ist jedoch Bestandteil der Enterprise Edition und wird nur mit einem Lizenzschlüssel wirksam. Wenn Sie bereits Authentik als eigenen Identity Provider betreiben, unterstützt Langfuse eine standardmäßige OIDC-Verbindung. Konten werden dann gemeinsam mit den Konten Ihrer übrigen Anwendungen verwaltet, statt in einer Kennwortliste zu verbleiben, die nur dieser Server kennt.

Senden Sie Ihren ersten Trace

Erstellen Sie in der Weboberfläche ein Projekt und kopieren Sie die öffentlichen und geheimen Schlüssel aus den Projekteinstellungen. Das Python SDK liest drei Umgebungsvariablen.

export LANGFUSE_PUBLIC_KEY="pk-lf-..."
export LANGFUSE_SECRET_KEY="sk-lf-..."
export LANGFUSE_BASE_URL="https://langfuse.example.com"

LANGFUSE_BASE_URL ist der Variablenname im SDK v4, das im März 2026 veröffentlicht wurde. Älterer Code und ältere Anleitungen verwenden LANGFUSE_HOST. Wenn Ihre Traces statt auf Ihrem Server in Langfuse Cloud ankommen, liegt das an einer nicht gesetzten Basis-URL, weil standardmäßig auf die gehostete Instanz verwiesen wird.

Das aktuelle Python SDK v5 erfordert Python 3.11 oder höher sowie ein selbst gehostetes Langfuse v4.59.0 oder höher. Diese Voraussetzung erfüllt der oben festgelegte Server.

pip install langfuse opentelemetry-instrumentation-anthropic anthropic
import os
from anthropic import Anthropic
from langfuse import get_client, observe
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor

AnthropicInstrumentor().instrument()
langfuse = get_client()
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

@observe(as_type="tool")
def lookup_order(order_id: str) -> str:
    return f"order {order_id}: shipped"

@observe()
def handle_request(question: str) -> str:
    context = lookup_order("A-1042")
    message = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=512,
        messages=[{"role": "user", "content": f"{context}\n\n{question}"}],
    )
    return message.content[0].text

if __name__ == "__main__":
    assert langfuse.auth_check()
    print(handle_request("Where is my order?"))
    langfuse.flush()

Der Dekorator @observe öffnet eine Observation um die Funktion, erfasst ihre Argumente und ihren Rückgabewert und verschachtelt sie unter der bereits aktiven Observation. AnthropicInstrumentor ist die OpenTelemetry-Instrumentierung für den Anthropic-Client. Sie wandelt jeden Aufruf von messages.create in eine Generation um, die den Modellnamen, die Token-Nutzung und die Latenz enthält. Am Aufruf selbst muss nichts geändert werden.

Zwei Aufrufe übernehmen die Prüfung für Sie. langfuse.auth_check() gibt bei ungültigen Schlüsseln oder einer falschen Basis-URL False zurück. Das ist schneller, als nach der Ursache für ein leeres Dashboard zu suchen. langfuse.flush() wartet, bis ausstehende Spans gesendet wurden. Kurzlebige Prozesse benötigen diesen Aufruf, weil das SDK im Hintergrund Stapel bildet und ein Skript, das sofort beendet wird, seinen noch nicht gesendeten Stapel mitnimmt. Wenn ein ganzes Team Traces statt nur ein einzelnes Skript sendet, setzen Sie diese drei Variablen einmal im Gateway, über das die Agents aller Teammitglieder ohnehin laufen, statt in der Shell jeder einzelnen Person. So hält ein selbst gehostetes OneCLI-Harness die Ausführungen aller Kollegen instrumentiert, während die Schlüssel an einer zentralen Stelle bleiben.

Warum wächst ClickHouse ständig?

Traces sind bei den meisten selbst gehosteten Systemen die am schnellsten wachsenden Daten. Jeder Agent-Lauf schreibt eine Zeile pro Schritt. Eingaben und Ausgaben werden vollständig gespeichert. Ein gesprächiger Agent mit langen Prompts erzeugt dadurch pro Tag deutlich mehr Daten als die Anwendung, die er überwacht. Wenn Sie nicht eingreifen, füllt ClickHouse den Datenträger. Ein voller Datenträger stoppt die Aufnahme, statt sie nur zu verlangsamen.

Hier wachsen zwei getrennte Dinge. Dafür sind zwei getrennte Maßnahmen erforderlich.

Zum einen wachsen Ihre eigenen Trace-Daten. Die passende Maßnahme ist die Einstellung für die Aufbewahrungsdauer. Bei einer selbst gehosteten Instanz ist die Datenaufbewahrung eine Funktion der Enterprise Edition und erfordert einen Lizenzschlüssel. Ohne Lizenz behält Langfuse Traces, Observations, Scores und Medien unbegrenzt. Planen Sie den Datenträger entsprechend und verlassen Sie sich auf den Datenträgeralarm am Ende dieses Abschnitts. Mit einer Lizenz öffnen Projektinhaber und Administratoren die Projekteinstellungen und legen dort eine Aufbewahrungsdauer in Tagen fest. Langfuse akzeptiert mindestens 3 Tage. Ein nächtlicher Job ermittelt anschließend Traces, Observations, Scores und Medienobjekte, die älter als dieses Zeitfenster sind, und löscht sie aus ClickHouse und dem Blob Storage. Davon ausgenommen sind Medien, die mit gespeicherten Dataset-Elementen verknüpft sind. Der Job benötigt die Berechtigung DeleteObject für den Bucket. Die MinIO-Root-Anmeldedaten in der standardmäßigen Compose-Datei verfügen bereits darüber. Die Löschung ist dauerhaft. Richten Sie daher zuerst einen Export in den Blob Storage ein, wenn Sie eine langfristige Historie benötigen. Schreiben Sie keine eigenen TTL-Klauseln für die Tabellen von Langfuse. Der Aufbewahrungsjob sorgt dafür, dass ClickHouse und der Bucket synchron bleiben. Eine manuelle TTL-Lösung löscht nur eine Seite.

Wählen Sie das Zeitfenster anhand Ihrer tatsächlichen Nutzung. Kosten- und Qualitätsprüfungen erfolgen anhand von Daten, die einige Tage alt sind, nicht anhand von Daten, die mehrere Monate alt sind. Dreißig Tage sind für ein kleines Team ein sinnvoller Anfang. 14 Tage reichen aus, wenn Sie einen Trace nur öffnen, wenn etwas fehlschlägt.

Zum anderen wachsen die eigenen Systemlogtabellen von ClickHouse. Das überrascht viele, weil der Datenträger auch nach dem Konfigurieren der Aufbewahrung weiter vollläuft. ClickHouse schreibt trace_log, text_log, opentelemetry_span_log, metric_log, asynchronous_metric_log und latency_log für die eigene Diagnose. Diese Tabellen werden ohne TTL ausgeliefert. Langfuse liest sie nie. Ermitteln Sie zuerst, wohin der Speicher tatsächlich gegangen ist.

SELECT table, formatReadableSize(size) AS size, rows FROM (
    SELECT table, database, sum(bytes) AS size, sum(rows) AS rows
    FROM system.parts
    WHERE active
    GROUP BY table, database
    ORDER BY size DESC
)

Führen Sie den Befehl mit docker compose exec clickhouse clickhouse-client --password "$CLICKHOUSE_PASSWORD" aus. Wenn sich Systemtabellen weit oben in der Liste befinden, deaktivieren Sie sie mit einem Konfigurations-Overlay. ClickHouse führt beim Start jede Datei in /etc/clickhouse-server/config.d/ mit seiner Hauptkonfiguration zusammen.

<clickhouse>
    <trace_log remove="1"/>
    <text_log remove="1"/>
    <opentelemetry_span_log remove="1"/>
    <asynchronous_metric_log remove="1"/>
    <metric_log remove="1"/>
    <latency_log remove="1"/>
</clickhouse>

Binden Sie das Overlay ein und starten Sie ClickHouse neu.

services:
  clickhouse:
    volumes:
      - ./clickhouse-config.d/system-logs.xml:/etc/clickhouse-server/config.d/system-logs.xml:ro

Damit werden keine neuen Einträge mehr geschrieben. Bereits auf dem Datenträger vorhandene Zeilen bleiben jedoch erhalten. Geben Sie den Speicher daher ausdrücklich mit DROP TABLE IF EXISTS system.trace_log frei. Verfahren Sie ebenso mit jeder Tabelle, die Sie entfernt haben. Wenn Sie die Diagnoseinformationen behalten möchten, verwenden Sie alternativ für jede Tabelle eine aggressive TTL anstelle von remove="1". Die Langfuse-Dokumentation zur Skalierung beschreibt dieses Verfahren ausführlich. Diese TTL-Anweisungen gelten nur, wenn eine Systemtabelle erstmals erstellt wird. Bei einer bestehenden Installation erklärt dieselbe Dokumentation daher auch, wie Sie die Tabellen nachträglich anpassen.

Eine weitere Tabelle sollten Sie kennen. blob_storage_file_log erfasst die in Ihren Bucket hochgeladenen Ereignisdateien. Wenn Sie für den Bucket zusätzlich eine Lifecycle-Richtlinie festlegen, versehen Sie die Tabelle mit einer passenden TTL. Andernfalls können beide Einstellungen auseinanderlaufen.

ALTER TABLE blob_storage_file_log MODIFY TTL created_at + INTERVAL 30 DAY DELETE;

Richten Sie außerdem einen einfachen df -h-Alarm für den Datenträger mit den Daten ein. Traces wachsen nicht gleichmäßig. Sie wachsen an dem Tag stark, an dem Sie einen neuen Agent ausrollen. Das erste Anzeichen dafür sollte kein Fehler bei der Datenaufnahme sein.

Postgres und ClickHouse sichern

Ein Langfuse-Backup besteht aus drei Teilen. Postgres enthält Ihre Benutzer, Organisationen, Projekte und API-Schlüssel. ClickHouse enthält die Traces. MinIO enthält die Rohdaten der Ereignisse. Wenn Sie nur Postgres wiederherstellen, erhalten Sie eine funktionierende Anmeldung, aber keine Historie. Wenn Sie nur ClickHouse wiederherstellen, erhalten Sie eine Historie, die niemand ohne Anmeldung anzeigen kann. Diese Aufteilung ist nicht auf Langfuse beschränkt. Auch ein selbst gehosteter Chatwoot-Servicedesk hat dieselbe Struktur: Ein Postgres-Dump ohne das Uploads-Verzeichnis stellt Unterhaltungen wieder her, deren Anhänge vollständig fehlen.

Postgres ist ein einfaches pg_dump. Genau dieses Verfahren verwenden die Langfuse-Backup-Dokumente für eine Docker-Installation.

docker compose exec -T postgres pg_dump -U postgres postgres \
  | gzip > langfuse-pg-$(date +%F).sql.gz

Bei ClickHouse ist mehr Vorsicht erforderlich, weil ein während laufender Merges kopiertes Datenverzeichnis kein konsistentes Backup ergibt. Der einfache Ansatz auf einem einzelnen Server besteht darin, den Container zu stoppen und das Volume zu archivieren.

docker compose stop clickhouse
docker volume ls | grep clickhouse
docker run --rm -v langfuse_langfuse_clickhouse_data:/data -v "$PWD":/backup alpine \
  tar czf /backup/langfuse-ch-$(date +%F).tar.gz -C /data .
docker compose start clickhouse

Verwenden Sie den Volumenamen, den docker volume ls ausgibt, nicht den Namen aus der YAML-Datei. Die Datei definiert langfuse_clickhouse_data, und Compose versieht diesen Namen mit dem Projektnamen. Ein Klon in einem Verzeichnis namens langfuse erzeugt daher langfuse_langfuse_clickhouse_data. Wenn der Name falsch ist, erstellt docker run kommentarlos ein neues leeres Volume, und Ihr Archiv enthält keine Daten.

Langfuse lädt die Rohdaten der Ereignisse bei jeder Ingestion-Anfrage in den Bucket hoch und liest sie erneut ein, um fehlgeschlagene Ingestion-Jobs zu wiederholen. Ein kurzer Stopp von ClickHouse bedeutet daher meist nur, dass der Worker die Verarbeitung anschließend wiederholt. Führen Sie ihn in einer ruhigen Zeit durch und halten Sie ihn kurz. Bei einer stärker ausgelasteten Instanz schreibt die eigene BACKUP DATABASE default TO S3(...)-Anweisung von ClickHouse ein konsistentes Backup, ohne den Server zu stoppen. MinIO ist der dritte Bestandteil. mc mirror oder die MinIO-Replikation in einen externen Bucket deckt diesen Teil ab. Unabhängig vom gewählten Verfahren müssen Sie die Backups vom Server übertragen. Dafür sind verschlüsselte restic-Backups auf einem VPS vorgesehen.

Redis enthält die Warteschlange und den Cache. Bei einem Verlust gehen wartende Jobs verloren, auch wenn ihre Rohdateien im Bucket weiterhin vorhanden sind.

Der Hinweis zur Konsistenz ist wichtig und sollte klar formuliert werden. Postgres und ClickHouse werden zu unterschiedlichen Zeitpunkten gesichert. Bei einer Wiederherstellung kann deshalb eine Projektzeile ohne Traces vorhanden sein oder Traces zu einem nicht mehr existierenden Projekt. Langfuse toleriert diesen Zustand. Erstellen Sie beide Dumps trotzdem in möglichst kurzem Abstand und während einer Phase mit wenig Datenverkehr. Der Ereignis-Bucket ist die eigentliche Sicherheitsreserve, weil Langfuse die Rohdaten der Ereignisse bei jeder Ingestion-Anfrage dorthin hochlädt.

Stellen Sie die Daten mindestens einmal in einem Test-Stack wieder her. So erkennen Sie einen falschen Volumenamen jetzt und nicht erst während eines Ausfalls.

Was Sie zuerst prüfen sollten

Vier Punkte sind in der ersten Woche besonders wichtig.

  • Kosten pro Trace. Langfuse berechnet die Kosten aus dem Modellnamen und dem Tokenverbrauch. Sortieren Sie die Traces daher nach Kosten und lesen Sie den teuersten Trace vollständig. Meist ist der Prompt zu groß geworden: Ein vollständiges Dokument wurde in den Kontext eingefügt oder ein Gesprächsverlauf wird nicht gekürzt. Sobald Sie das sehen, wird die Kontrolle der Kosten eines AI-Agenten zu einer Engineering-Aufgabe statt zu einer Schätzung.
  • Tokenverbrauch getrennt nach Input und Output. Input-Tokens sind zahlreich und günstig. Output-Tokens sind weniger zahlreich und teuer. Gecachter Input ist noch günstiger. Die gleiche Abrechnung wird in der Berechnung des Tokenverbrauchs von Claude Code erläutert. Sie gilt für jeden Agenten, den Sie selbst schreiben.
  • Latenz-Perzentile. Der Median verdeckt das Problem. Bei p95 und p99 treten die Timeouts auf. Innerhalb einer Agentenschleife wird ein langsamer Tool-Aufruf bei p95 mit der Anzahl der Iterationen multipliziert.
  • Fehlgeschlagene Tool-Aufrufe. Filtern Sie Beobachtungen nach der Stufe ERROR. Ein Tool, das in 5% der Fälle fehlschlägt, bleibt in einer aggregierten Erfolgsrate unsichtbar. In den Traces ist das Problem dagegen deutlich sichtbar, wenn Sie beobachten, wie das Modell den Aufruf wiederholt und anschließend Tokens für eine Umgehung verbraucht.

Legen Sie das Aufbewahrungsfenster fest und wählen Sie das Dashboard aus, das Sie wöchentlich am selben Tag prüfen. Tun Sie das am besten an dem Tag, an dem Sie den Dienst bereitstellen. Ein Observability-Tool, das niemand öffnet, ist eine Datenbank, die eine Festplatte füllt.

FAQ

Wie viel Arbeitsspeicher benötigt ein selbst gehostetes Langfuse?

Planen Sie 4 CPU-Kerne und 16 GiB Arbeitsspeicher ein. Das entspricht der Empfehlung des Langfuse-Docker-Compose-Leitfadens für eine einzelne virtuelle Maschine. Zusätzlich sollten Sie etwa 100 GiB Speicherplatz vorsehen. Die veröffentlichten Mindestanforderungen der Komponenten betragen 8 GiB für ClickHouse sowie jeweils 4 GiB für die Web- und Worker-Container. Auf derselben Seite werden 4 GiB für Postgres, 1.5 GiB für Redis und 4 GiB für MinIO genannt. Zusammen benötigen alle sechs Komponenten 25.5 GiB. Das liegt über der Compose-Empfehlung von 16 GiB.

Warum läuft meine ClickHouse-Festplatte weiter voll, nachdem ich die Datenaufbewahrung konfiguriert habe?

Die Einstellung zur Datenaufbewahrung gilt nur für die eigenen Daten von Langfuse. ClickHouse schreibt zusätzlich die Diagnosetabellen trace_log, text_log, opentelemetry_span_log, metric_log, asynchronous_metric_log und latency_log. Diese Tabellen werden ohne TTL ausgeliefert. Führen Sie system.parts nach Tabelle gruppiert aus, um die größte Tabelle zu ermitteln. Deaktivieren Sie anschließend nicht benötigte Tabellen mit einem remove="1"-Eintrag in einer Datei unter /etc/clickhouse-server/config.d/. Starten Sie ClickHouse neu und löschen Sie die vorhandenen Tabellen, um den bereits belegten Speicherplatz freizugeben.

Wie lange ist die minimale Datenaufbewahrungsdauer in Langfuse?

Drei Tage. Die Aufbewahrungsdauer wird pro Projekt in den Projekteinstellungen oder über die Organization Management API festgelegt. Ein nächtlicher Job löscht Traces, Beobachtungen, Scores und Medienobjekte, die älter als der konfigurierte Zeitraum sind, aus ClickHouse und dem Blob Storage. Davon ausgenommen sind Medien, die mit gespeicherten Dataset-Elementen verknüpft sind. Bei einer selbst gehosteten Instanz ist diese Funktion Bestandteil der Enterprise Edition und erfordert einen Lizenzschlüssel. Ohne Lizenzschlüssel werden die Daten unbegrenzt aufbewahrt. Die Löschung kann nicht rückgängig gemacht werden. Konfigurieren Sie daher zuerst einen Export in den Blob Storage, wenn Sie den Verlauf über diesen Zeitraum hinaus benötigen.

Muss ich sowohl Postgres als auch ClickHouse sichern?

Ja, denn die beiden Systeme enthalten unterschiedliche Daten. Postgres speichert Benutzer, Organisationen, Projekte und API-Schlüssel. ClickHouse speichert die Trace-Daten selbst. Eine Wiederherstellung nur von Postgres ergibt eine Instanz, bei der Sie sich anmelden können, die jedoch keine Inhalte enthält. Sichern Sie außerdem den MinIO-Bucket. Er enthält die Rohereignisse, die Langfuse beim Eingang dauerhaft speichert, und ist damit im Stack am ehesten eine Quelle der Wahrheit.

Kann ich eine vorhandene OpenTelemetry-Konfiguration auf ein selbst gehostetes Langfuse richten?

Ja. Langfuse v4 und die aktuellen SDKs basieren auf OpenTelemetry. Die Anthropic- und OpenAI-OTel-Instrumentierungen exportieren direkt dorthin. Führen Sie in Python pip install langfuse opentelemetry-instrumentation-anthropic aus, rufen Sie AnthropicInstrumentor().instrument() einmal beim Start auf und setzen Sie LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY und LANGFUSE_BASE_URL auf Ihren eigenen Host. Prüfen Sie die Konfiguration mit langfuse.auth_check(), bevor Sie nach einem fehlenden Dashboard suchen.

War dieser Artikel hilfreich?