SSD Nodes Learn Hosting plans →
गाइड Matt Connorलेखक: Matt Connor

Langfuse को अपने VPS पर कैसे host करें

अपने VPS पर Langfuse को self-host करने का पूरा तरीका जानें। इसमें ClickHouse डेटा रिटेंशन, TLS सेटअप, इमेज टैग्स और सुरक्षित बैकअप लेने की सटीक प्रक्रिया समझाई गई है।

AI agent को trace क्यों करें

आप Langfuse को self-host करते हैं ताकि यह देख सकें कि आपके agent ने वास्तव में एक run के दौरान क्या किया। Langfuse एक open source LLM (large language model) observability tool है। यह हर prompt, हर model response, हर tool call और हर token को record करता है, फिर उन्हें एक trace के अंतर्गत group करता है जिसे आप खोलकर पढ़ सकते हैं। इसे अपने VPS पर चलाने का अर्थ है कि वे prompts कभी भी आपके नियंत्रण वाले सर्वर से बाहर नहीं जाते।

इसकी आवश्यकता स्पष्ट है। आप किसी cost problem या quality problem को ठीक नहीं कर सकते जिसे आप देख नहीं सकते। एक provider invoice आपको बताती है कि मंगलवार का खर्च सोमवार की तुलना में चार गुना था। एक trace आपको बताती है कि किस agent run ने ऐसा किया, कौन सा prompt बढ़कर 40,000 tokens का हो गया, और कौन सा retry loop नौ बार चलने के बाद विफल हुआ। Invoice आपको केवल संख्या देती है। Trace आपको वह code देती है जिसने उसे उत्पन्न किया।

इस guide में तीन शब्दों का प्रयोग किया गया है। Trace आपके agent का एक end-to-end run है। Observation उस run के भीतर का एक step है: सामान्य code के लिए एक span, और model को की गई call के लिए एक generation। Score एक trace से जुड़ी संख्या है, जो human review या automated evaluator से प्राप्त होती है। Langfuse, OpenTelemetry (OTel) का उपयोग करता है, जो distributed tracing के लिए vendor-neutral standard है, इसलिए जो instrumentation आपके पास पहले से मौजूद है, वह इसकी ओर point कर सकता है।

Langfuse self-hosting वास्तव में क्या चलाता है

Langfuse v4 एक कंटेनर नहीं है। यह दो एप्लिकेशन कंटेनर और चार स्टोरेज सेवाओं से बना है, और एक ही VPS पर ये सभी छह आपके सर्वर पर चलते हैं।

  • langfuse-web वेब इंटरफेस और ingestion API को सर्व करता है।
  • langfuse-worker बैकग्राउंड में कतार (queue) को खाली करता है। यह ingestion batches को पार्स करता है, लागत की गणना करता है, और nightly retention job चलाता है।
  • Postgres ट्रांजेक्शनल डेटा जैसे कि यूजर्स, ऑर्गनाइजेशन, प्रोजेक्ट्स, API keys और प्रॉम्प्ट्स को रखता है।
  • ClickHouse स्वयं ट्रेस डेटा को रखता है, जिसका अर्थ है ऑब्जर्वेशन और स्कोर। यह विश्लेषणात्मक क्वेरी के लिए बनाया गया एक कॉलम स्टोर है, यही कारण है कि दस करोड़ पंक्तियों वाला डैशबोर्ड भी तेजी से परिणाम देता है।
  • Redis वह कतार और कैश है जो वेब और वर्कर के बीच स्थित होता है।
  • MinIO आपको सर्वर पर S3-compatible ऑब्जेक्ट स्टोरेज देता है। यह हर आने वाले रॉ इवेंट और आपके द्वारा अटैच की गई किसी भी मीडिया फाइल को रखता है।

Langfuse अपने द्वारा चलाए जाने वाले प्रत्येक घटक के लिए न्यूनतम संसाधनों को प्रकाशित करता है। चार्ट उन तीन घटकों को दिखाता है जो मुख्य कार्य करते हैं।

चार्टLangfuse published minimum resources per component
इस चार्ट के पीछे का डेटा
[
  {
    "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 को 8 GiB मेमोरी की आवश्यकता होती है। वेब कंटेनर और वर्कर को प्रत्येक के लिए 4 GiB की आवश्यकता होती है। ये चार्ट में मौजूद 3 घटकों के लिए प्रकाशित न्यूनतम सीमाएं हैं। वही पेज Postgres के लिए 4 GiB, Redis या Valkey के लिए 1.5 GiB और MinIO के लिए 4 GiB सूचीबद्ध करता है, इसलिए सभी छह के लिए न्यूनतम योग 25.5 GiB होता है। प्रोजेक्ट की अपनी Docker Compose गाइड एक सिंगल मशीन के लिए कम से कम 4 कोर, 16 GiB मेमोरी और लगभग 100 GiB स्टोरेज की सिफारिश करती है। यह घटकों के न्यूनतम योग से कम है, इसलिए 16 GiB को एक साझा Compose बॉक्स के लिए न्यूनतम आवश्यकता मानें, न कि अतिरिक्त क्षमता (headroom)।

इसे 2 GiB वाले प्लान पर चलाने का प्रयास न करें। ClickHouse शुरू होता है, कुछ समय के लिए राइट्स स्वीकार करता है, और फिर बैकग्राउंड मर्ज के दौरान क्रैश हो जाता है, क्योंकि मर्ज टेबल के बड़े हिस्सों को मेमोरी में लोड करता है। आप देखेंगे कि docker compose ps, clickhouse कंटेनर को restarting के रूप में रिपोर्ट कर रहा है, dmesg में Out of memory: Killed process 1234 (clickhouse-serv) जैसी लाइन है, और हर Langfuse डैशबोर्ड 500 एरर दे रहा है। कम दबाव में ClickHouse क्वेरी को अस्वीकार कर देता है और DB::Exception: Memory limit (total) exceeded लॉग करता है। 16 GiB वह संख्या है जिसके लिए आपको योजना बनानी चाहिए। यदि उसी VPS पर कुछ और भी चलाना है, तो उसके लिए अलग से बजट रखें, क्योंकि a self-hosted AFFiNE workspace जैसा अपेक्षाकृत हल्का स्टैक भी अपनी अलग मेमोरी चाहता है और ClickHouse अपनी मेमोरी वापस नहीं छोड़ेगा।

Docker Compose के साथ Langfuse को Deploy करें

Repository को clone करें। Stack, wiring और default environment सभी इसके docker-compose.yml में मौजूद हैं।

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

आपको जिन मानों (values) को बदलना है, वे उस फ़ाइल में # CHANGEME के रूप में चिह्नित हैं। सबसे पहले तीन application secrets जनरेट करें।

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

ENCRYPTION_KEY 256 bits का होना चाहिए जिसे 64 hex characters के रूप में लिखा जाता है, जो कि बिल्कुल वही है जिसे openssl rand -hex 32 प्रिंट करता है। यह stored sensitive values को encrypt करता है, जिसमें आपके द्वारा instance में store की गई कोई भी LLM provider keys शामिल हैं। डेटा मौजूद होने के बाद इसे बदलने पर वे rows decrypt नहीं हो पाएंगी, इसलिए इसे पहले boot से ही स्थायी (permanent) मानें। SALT का उपयोग आपकी Langfuse API keys को hash करने के लिए किया जाता है, इसलिए इसे बदलने से वे सभी keys अमान्य हो जाएंगी जिनका उपयोग आपके agents पहले से कर रहे हैं।

इसके बाद POSTGRES_PASSWORD, CLICKHOUSE_PASSWORD, REDIS_AUTH और MINIO_ROOT_PASSWORD को सेट करें। MinIO password चार स्थानों पर दिखाई देता है: एक बार MINIO_ROOT_PASSWORD के रूप में, फिर LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY, LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY और LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY के रूप में। यदि एक भी छूट गया, तो MinIO उस client को SignatureDoesNotMatch के साथ अस्वीकार कर देगा, जो worker log में दिखाई देगा जबकि web interface अभी भी सही चल रहा होगा। इन मानों को tracked compose file के बजाय env file में रखने का तरीका Docker Compose env files and secrets में बताया गया है।

शुरू करने से पहले image tags को पिन करें

दी गई फ़ाइल docker.langfuse.com/langfuse/langfuse:4 और docker.langfuse.com/langfuse/langfuse-worker:4 का उपयोग करती है। वे tags बदलते रहते हैं। Langfuse शुरू होने पर अपने Postgres और ClickHouse migrations को स्वचालित रूप से चलाता है, इसलिए महीनों बाद किया गया एक सामान्य docker compose pull उस डेटाबेस पर अनियोजित schema migration बन सकता है जिसका आपने उस सुबह backup नहीं लिया था। दोनों को एक release पर docker-compose.override.yml में पिन करें, जिसे Compose दी गई फ़ाइल के ऊपर merge कर देता है ताकि बाद का git pull आपके संपादन (edits) के साथ संघर्ष न करे। वही images Docker Hub पर प्रकाशित होती हैं, जहाँ से नीचे दिया गया override उन्हें pull करता है।

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

अगस्त 2026 तक Version 4.3.1 वर्तमान 4.3 release था। उसके बाद से releases तेजी से आए हैं, और 11 अक्टूबर, 2026 तक GitHub पर v4.56.0 नवीनतम था। ऊपर दिया गया override v4.72.0 का उपयोग करता है, जो 31 अक्टूबर, 2026 तक GitHub पर नवीनतम था। project के GitHub releases page को देखें, जिस दिन आप deploy कर रहे हैं उस दिन जो वर्तमान हो उसे पिन करें, फिर उस नंबर को सोच-समझकर बदलें। दी गई फ़ाइल में कुछ storage images भी इसी तरह के व्यवहार की हकदार हैं, क्योंकि उनके tags भी बदलते रहते हैं। postgres:17 (एक POSTGRES_VERSION variable का default) और redis:7 एक major version का पालन करते हैं। दी गई फ़ाइल अब docker.io/clickhouse/clickhouse-server के लिए एक सटीक version tag का उपयोग करती है, जो एक सटीक ClickHouse version को पिन करता है। यह नियम केवल Langfuse के लिए विशिष्ट नहीं है: एक self-hosted openGym workout tracker इस stack का एक हिस्सा चलाता है और फिर भी उसे एक named git tag की आवश्यकता होती है, क्योंकि जो कुछ भी शुरू होने पर अपने डेटाबेस को migrate करता है, वह एक सामान्य pull को schema change में बदल सकता है।

इसे शुरू करें।

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

पहला boot migrations चलाता है, इसलिए किसी भी प्रतिक्रिया के आने से पहले दो या तीन मिनट का समय दें। एक बार शुरू होने के बाद web container Ready लॉग करता है। docker compose ps को running स्थिति में छह services की सूची दिखानी चाहिए। यदि कोई container लूप में restart होता है, तो उसका log कारण बताता है। ClickHouse migrations तब लागू होते हैं जब web container शुरू होता है, इसलिए एक खराब CLICKHOUSE_MIGRATION_URL docker compose logs langfuse-web में दिखाई देता है, न कि उस worker log में जिसे आप tail कर रहे हैं। वह variable port 9000 पर ClickHouse native protocol का उपयोग करता है, न कि HTTP port 8123 का, और इसे 8123 पर इंगित करने से web container का startup migration विफल हो जाता है। worker धीमी background migrations चलाता है, इसलिए उसका log वह स्थान है जहाँ वे रिपोर्ट करते हैं।

स्वयं box से health की जाँच करें।

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

एक साधारण /api/public/health कॉल केवल यह साबित करती है कि API process जीवित है, क्योंकि default रूप से यह डेटाबेस की जाँच नहीं करती है। failIfDatabaseUnavailable=true फॉर्म वह है जिस पर monitor को इंगित करना सार्थक है, और डेटाबेस के पहुँच से बाहर होने पर यह 503 लौटाता है। जब web container traffic प्राप्त करने के लिए तैयार होता है तो /api/public/ready 200 लौटाता है, और बंद होने का निर्देश मिलने पर 500 लौटाता है। दोनों सामान्य HTTP checks हैं, इसलिए एक Uptime Kuma status page उन्हें देख सकता है और आपके agents के पता लगाने से पहले ही आपको बता सकता है कि stack down है।

TLS को सामने रखें और अतिरिक्त ports को बंद करें

प्रदान की गई compose file वेब कंटेनर के लिए 3000:3000 और MinIO के लिए 9090:9000 को publish करती है। दोनों ही सभी interfaces पर bind होते हैं। एक public IP पर इसका मतलब है कि port 3000 को scan करने वाला कोई भी व्यक्ति आपके sign up page तक पहुँच सकता है, और 9090 को scan करने वाला कोई भी व्यक्ति उस bucket से बात कर रहा है जिसमें आपके raw prompts मौजूद हैं।

केवल एक firewall rule इन्हें बंद नहीं करता है। Docker अपने स्वयं के DNAT rules को nat table में लिखता है, और ufw के filter rules द्वारा packet को देखने से पहले ही उनका मूल्यांकन हो जाता है, इसलिए ufw deny 3000 published port को खुला छोड़ देता है। यह समस्या इतने लोगों को प्रभावित करती है कि इसके लिए एक अलग guide मौजूद है: Docker द्वारा published ports ufw को क्यों bypass करते हैं। अपनी override file में loopback पर bind करें। published ports को बदलने के लिए Docker Compose 2.24.4 या उसके बाद के संस्करण के साथ !override का उपयोग करें; एक साधारण list मूल bindings के साथ merge हो जाती है।

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 को scheme सहित सटीक public address होना चाहिए, क्योंकि login flow उस value से अपना callback URL बनाता है। यदि आप इसे HTTPS proxy के पीछे http://localhost:3000 के रूप में छोड़ते हैं, तो sign in round trip browser को ऐसी जगह भेज देगा जहाँ वह पहुँच नहीं सकता।

अब एक reverse proxy को 127.0.0.1:3000 पर point करें और उसे certificate संभालने दें। यदि Traefik उसी Compose project में है, तो एक Traefik reverse proxy के पीछे कई apps चलाना में बताए गए labels का उपयोग करके Compose network के माध्यम से langfuse-web:3000 पर route करें। यदि Langfuse उस box पर एकमात्र service है, तो Caddy दो lines में वही काम कर देता है। curl -sI https://langfuse.example.com/api/public/ready के साथ verify करें, फिर किसी दूसरी machine से पुष्टि करें कि curl http://YOUR_IP:3000 connect नहीं हो पा रहा है।

MinIO के संबंध में एक सावधानी बरतें। Langfuse उस S3 endpoint की ओर इशारा करते हुए presigned URLs प्रदान करता है, जो media upload करने वाले SDKs और उसे load करने वाले आपके browser, दोनों के लिए होते हैं। इसलिए यदि आप images या audio वाले multi-modal traces का उपयोग करते हैं, तो केवल loopback पर चलने वाला MinIO का मतलब होगा कि वे attachments दूसरी machines से upload नहीं होंगी या आपके browser में load नहीं होंगी। इसे proxy करने से पहले blob storage configuration page को पढ़ें, क्योंकि presigned URL में लिखा गया endpoint, LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT, आपके द्वारा publish किए गए endpoint से मेल खाना चाहिए। Plain text traces इससे प्रभावित नहीं होते हैं।

पहली बार visit करने पर अपना account बनाएँ, फिर instance को केवल अपने तक सीमित रखें। अपना account बनने के बाद AUTH_DISABLE_SIGNUP=true set करें, ताकि page तक पहुँचने वाला कोई अनजान व्यक्ति register न कर सके। Langfuse में LANGFUSE_ALLOWED_ORGANIZATION_CREATORS भी है, जो यह सीमित करता है कि कौन organizations बना सकता है, लेकिन यह एक Enterprise Edition feature है और केवल license key के साथ ही प्रभावी होता है। यदि आप पहले से ही Authentik को अपने identity provider के रूप में चला रहे हैं, तो Langfuse एक standard OIDC connection स्वीकार करता है, जिससे accounts आपके अन्य apps के साथ manage होते हैं, न कि केवल इस box की password list में रहते हैं।

अपनी पहली trace भेजें

Web interface में एक project बनाएँ और project settings से उसकी public और secret keys को copy करें। Python SDK तीन environment variables को पढ़ता है।

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

LANGFUSE_BASE_URL, SDK v4 में variable का नाम है, जिसे March 2026 में release किया गया था। पुराना code और पुरानी guides LANGFUSE_HOST का उपयोग करती हैं। यदि आपकी traces आपके सर्वर के बजाय Langfuse Cloud पर जा रही हैं, तो इसका कारण unset base URL है, क्योंकि default setting hosted instance की ओर point करती है।

वर्तमान Python SDK v5 के लिए Python 3.11 या बाद का version और self-hosted Langfuse v4.59.0 या बाद का version आवश्यक है, जिसे ऊपर दिया गया pinned सर्वर पूरा करता है।

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()

@observe decorator function के चारों ओर एक observation खोलता है, उसके arguments और return value को capture करता है, और उसे किसी भी सक्रिय observation के अंतर्गत nest कर देता है। AnthropicInstrumentor, Anthropic client के लिए OpenTelemetry instrumentation है, और यह प्रत्येक messages.create call को एक generation में बदल देता है जिसमें model का नाम, token usage और latency शामिल होती है, बिना call site में कोई बदलाव किए।

दो calls आपके लिए जाँच (checking) करती हैं। langfuse.auth_check() गलत keys या गलत base URL होने पर False return करता है, जो यह सोचने से बेहतर है कि dashboard खाली क्यों है। langfuse.flush() queued spans के भेजे जाने तक process को block कर देता है, और short-lived processes के लिए इसकी आवश्यकता होती है, क्योंकि SDK background में batching करता है और जो script तुरंत exit हो जाती है, वह अपने unsent batch को भी साथ ले जाती है। यदि कोई पूरी टीम script के बजाय traces भेज रही है, तो उन तीन variables को हर व्यक्ति के shell में सेट करने के बजाय उस gateway में एक बार सेट करें जिससे सभी के agents पहले से गुजरते हैं, यही कारण है कि a self-hosted OneCLI harness हर सहकर्मी के runs को instrumented रखता है जबकि keys एक ही जगह सुरक्षित रहती हैं।

ClickHouse का आकार लगातार क्यों बढ़ता रहता है?

Traces सबसे तेजी से बढ़ने वाला डेटा है जिसे अधिकांश लोग खुद होस्ट (self-host) करते हैं। प्रत्येक agent run हर स्टेप के लिए एक रो (row) लिखता है, और इनपुट व आउटपुट को पूरा स्टोर किया जाता है। इसलिए, लंबे प्रॉम्प्ट वाला एक चैटी एजेंट उस एप्लिकेशन की तुलना में प्रतिदिन कहीं अधिक बाइट्स उत्पन्न करता है जिसकी वह निगरानी कर रहा है। यदि इसे ऐसे ही छोड़ दिया जाए, तो ClickHouse डिस्क को भर देता है, और डिस्क फुल होने पर ingestion धीमा होने के बजाय पूरी तरह रुक जाता है।

यहाँ दो अलग-अलग चीजें बढ़ती हैं, और उन्हें दो अलग-अलग समाधानों की आवश्यकता है।

पहली चीज आपका अपना trace डेटा है, और इसका समाधान retention सेटिंग है। एक self-hosted इंस्टेंस पर, डेटा retention एक Enterprise Edition फीचर है और इसके लिए एक license key की आवश्यकता होती है। इसके बिना, Langfuse ट्रेसेस, ऑब्जर्वेशन, स्कोर और मीडिया को अनिश्चित काल तक रखता है। इसलिए, डिस्क का आकार उसी के अनुसार रखें और इस सेक्शन के अंत में दिए गए डिस्क अलर्ट पर भरोसा करें। लाइसेंस होने पर, प्रोजेक्ट के मालिक और एडमिन प्रोजेक्ट सेटिंग्स खोलकर दिनों में डेटा retention अवधि सेट कर सकते हैं। Langfuse कम से कम 3 दिनों की अवधि स्वीकार करता है। इसके बाद एक nightly job उस विंडो से पुराने ट्रेसेस, ऑब्जर्वेशन, स्कोर और मीडिया एसेट्स का चयन करती है और उन्हें ClickHouse तथा blob storage से हटा देती है, सिवाय उन मीडिया फाइलों के जो सेव किए गए dataset आइटम्स से जुड़ी हैं। इस जॉब को बकेट पर DeleteObject अनुमति की आवश्यकता होती है, जो डिफ़ॉल्ट compose फाइल में MinIO root क्रेडेंशियल्स के पास पहले से होती है। विलोपन (deletion) स्थायी होता है, इसलिए यदि आपको दीर्घकालिक इतिहास की आवश्यकता है तो पहले blob storage एक्सपोर्ट कॉन्फ़िगर करें। Langfuse की अपनी टेबल्स पर मैन्युअल रूप से TTL क्लॉज न लिखें: retention जॉब ही ClickHouse और बकेट को सिंक में रखती है, और मैन्युअल TTL केवल एक तरफ का डेटा हटाता है।

आप वास्तव में जितना उपयोग करते हैं, उसी के अनुसार विंडो चुनें। लागत और गुणवत्ता की समीक्षा कुछ दिन पुराने डेटा पर होती है, महीनों पुराने डेटा पर नहीं। एक छोटी टीम के लिए 30 दिन एक उचित शुरुआत है, और यदि आप केवल तभी ट्रेस खोलते हैं जब कुछ खराब होता है, तो 14 दिन पर्याप्त हैं।

दूसरी चीज ClickHouse की अपनी सिस्टम लॉग टेबल्स हैं, और यह लोगों को हैरान करती है, क्योंकि retention कॉन्फ़िगर होने के बाद भी डिस्क बढ़ती रहती है। ClickHouse अपने डायग्नोस्टिक्स के लिए trace_log, text_log, opentelemetry_span_log, metric_log, asynchronous_metric_log और latency_log लिखता है, ये बिना किसी TTL के आते हैं, और Langfuse इन्हें कभी नहीं पढ़ता है। सबसे पहले यह पता लगाएं कि डिस्क स्पेस वास्तव में कहाँ खर्च हो रहा है।

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
)

इसे docker compose exec clickhouse clickhouse-client --password "$CLICKHOUSE_PASSWORD" के साथ चलाएं। यदि सिस्टम टेबल्स सूची में सबसे ऊपर हैं, तो उन्हें कॉन्फ़िगरेशन ओवरले के साथ बंद कर दें, क्योंकि ClickHouse स्टार्टअप पर /etc/clickhouse-server/config.d/ में मौजूद हर फाइल को अपने मुख्य कॉन्फ़िगरेशन के ऊपर मर्ज करता है।

<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>

इसे माउंट करें और ClickHouse को रीस्टार्ट करें।

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

यह नई राइट्स को रोक देता है। डिस्क पर मौजूद पुरानी रो वहीं रहती हैं, इसलिए DROP TABLE IF EXISTS system.trace_log के साथ स्पेस को स्पष्ट रूप से रिक्लेम करें और यही प्रक्रिया उन सभी टेबल्स के लिए दोहराएं जिन्हें आपने हटाया है। यदि आप डायग्नोस्टिक्स रखना चाहते हैं, तो विकल्प यह है कि remove="1" के बजाय प्रत्येक टेबल पर एक आक्रामक TTL सेट करें, जिसे Langfuse स्केलिंग डॉक्स में विस्तार से बताया गया है। ये TTL निर्देश केवल तभी लागू होते हैं जब कोई सिस्टम टेबल पहली बार बनाई जाती है, इसलिए मौजूदा इंस्टॉलेशन पर उन्हीं डॉक्स में टेबल्स को रेट्रोफिट करने का तरीका भी बताया गया है।

एक और टेबल है जिसके बारे में जानना उपयोगी है। blob_storage_file_log आपकी बकेट में अपलोड की गई इवेंट फाइल्स को ट्रैक करती है। यदि आप बकेट पर lifecycle पॉलिसी भी सेट करते हैं, तो टेबल को एक मैचिंग TTL दें ताकि दोनों में अंतर न आए।

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

डेटा डिस्क पर एक साधारण df -h अलर्ट भी लगाएं। ट्रेसेस सुचारू रूप से नहीं बढ़ते हैं। वे उस दिन बढ़ते हैं जिस दिन आप एक नया एजेंट शिप करते हैं, और इसका पहला संकेत ingestion फेल होना नहीं होना चाहिए।

Postgres और ClickHouse का बैकअप लेना

Langfuse बैकअप के तीन हिस्से होते हैं। Postgres आपके users, organisations, projects और API keys को सुरक्षित रखता है। ClickHouse traces को रखता है। MinIO raw events को स्टोर करता है। यदि आप केवल Postgres को restore करते हैं, तो आप login तो कर पाएंगे लेकिन कोई history नहीं दिखेगी। यदि आप केवल ClickHouse को restore करते हैं, तो history तो दिखेगी लेकिन कोई भी login नहीं कर पाएगा। यह विभाजन केवल Langfuse तक सीमित नहीं है, और एक self-hosted Chatwoot support desk का ढांचा भी ऐसा ही है, जहाँ बिना uploads directory के लिया गया Postgres dump उन conversations को restore करता है जिनके attachments गायब हो चुके होते हैं।

Postgres एक साधारण pg_dump है, जिसका उपयोग Langfuse बैकअप docs में Docker install के लिए किया जाता है।

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

ClickHouse के लिए अधिक सावधानी की आवश्यकता होती है, क्योंकि merges के दौरान copy की गई live data directory एक consistent बैकअप नहीं होती। एक ही box पर इसका सरल तरीका यह है कि container को stop करें और volume को archive कर लें।

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

docker volume ls द्वारा print किए गए volume name का उपयोग करें, न कि YAML में लिखे गए नाम का। फाइल में langfuse_clickhouse_data घोषित होता है, और Compose इसमें project name का prefix जोड़ देता है, इसलिए langfuse नामक directory में clone करने पर langfuse_langfuse_clickhouse_data बनता है। यदि आप इसमें गलती करते हैं, तो docker run बिना किसी चेतावनी के एक नया खाली volume बना देगा, और आपके archive में कुछ भी नहीं होगा।

Langfuse हर ingestion request पर raw event data को bucket में upload करता है और failed ingestion jobs को retry करने के लिए उसे फिर से पढ़ता है, इसलिए ClickHouse को थोड़ी देर के लिए stop करने का मतलब है कि worker बाद में retry कर लेगा। इसे कम traffic वाले समय में करें और प्रक्रिया को संक्षिप्त रखें। अधिक व्यस्त instance के लिए, ClickHouse का अपना BACKUP DATABASE default TO S3(...) statement सर्वर को stop किए बिना एक consistent बैकअप तैयार करता है। MinIO तीसरा हिस्सा है, और mc mirror या किसी off-box bucket पर MinIO replication इसे कवर करता है। आप जो भी बैकअप लें, उसे सर्वर से बाहर निकालें, जिसके लिए VPS पर encrypted restic backups का उपयोग किया जाता है।

Redis queue और cache को संभालता है। इसे खोने का मतलब है queued jobs का नष्ट होना, भले ही उनके raw event files bucket में मौजूद रहें।

consistency की चेतावनी वास्तविक है और इसे स्पष्ट रूप से समझना आवश्यक है। Postgres और ClickHouse के dumps अलग-अलग समय पर लिए जाते हैं, इसलिए restore करने पर ऐसा हो सकता है कि किसी project row के traces न मिलें, या ऐसे traces मिलें जो किसी ऐसे project के हों जो अब मौजूद नहीं है। Langfuse इसे सहन कर लेता है, लेकिन दोनों dumps को एक-दूसरे के करीब और कम-traffic वाले समय में लें। Event bucket ही असली सुरक्षा कवच है, क्योंकि Langfuse हर ingestion request पर raw event data को वहां upload करता है।

कम से कम एक बार scratch stack में restore करके देखें। इसी तरह आप outage के दौरान नहीं, बल्कि अभी गलत volume name जैसी समस्याओं का पता लगा सकते हैं।

सबसे पहले क्या देखें

पहले सप्ताह में चार चीजें महत्वपूर्ण होती हैं।

  • प्रति ट्रेस लागत (Cost per trace): Langfuse मॉडल के नाम और टोकन उपयोग के आधार पर लागत की गणना करता है। इसलिए, ट्रेसेस को लागत के अनुसार क्रमबद्ध करें और सबसे महंगे ट्रेस को शुरू से अंत तक पढ़ें। इसका कारण आमतौर पर एक बढ़ता हुआ प्रॉम्प्ट होता है: संदर्भ (context) में पेस्ट किया गया पूरा दस्तावेज़, या बातचीत का ऐसा इतिहास जिसे कोई ट्रिम नहीं करता। एक बार जब आप इसे देख लेते हैं, तो AI एजेंट की लागत को नियंत्रित करना अनुमान लगाने के बजाय एक इंजीनियरिंग कार्य बन जाता है।
  • इनपुट और आउटपुट के आधार पर टोकन उपयोग का विभाजन: इनपुट टोकन संख्या में अधिक और सस्ते होते हैं, आउटपुट टोकन कम और महंगे होते हैं, और कैश किया गया इनपुट और भी सस्ता होता है। यही हिसाब Claude Code टोकन उपयोग की गणना कैसे की जाती है में समझाया गया है, और यह आपके द्वारा लिखे गए किसी भी एजेंट पर लागू होता है।
  • लेटेंसी पर्सेंटाइल (Latency percentiles): माध्यिका (median) समस्या को छिपा देती है। p95 और p99 वे स्थान हैं जहाँ टाइमआउट होते हैं, और एक एजेंट लूप के भीतर, p95 पर एक धीमा टूल कॉल पुनरावृत्तियों (iterations) की संख्या से गुणा हो जाता है।
  • विफल टूल कॉल (Failed tool calls): ऑब्जर्वेशन को ERROR स्तर के आधार पर फ़िल्टर करें। जो टूल 5% समय विफल होता है, वह कुल सफलता दर में अदृश्य रहता है, लेकिन ट्रेसेस में बहुत स्पष्ट दिखाई देता है, जहाँ आप मॉडल को पुनः प्रयास करते हुए और फिर उसे ठीक करने के लिए टोकन खर्च करते हुए देखते हैं।

रिटेंशन विंडो सेट करें और वह डैशबोर्ड चुनें जिसे आप हर सप्ताह उसी दिन चेक करेंगे जिस दिन आप डिप्लॉय करते हैं। एक ऑब्जर्वेबिलिटी टूल जिसे कोई नहीं खोलता, वह केवल एक डेटाबेस है जो डिस्क को भरता है।

FAQ

self-hosted Langfuse के लिए कितनी memory की आवश्यकता होती है?

4 CPU cores और 16 GiB memory की योजना बनाएं, जो कि एक single virtual machine के लिए Langfuse Docker Compose गाइड द्वारा अनुशंसित है, साथ ही लगभग 100 GiB storage की आवश्यकता होगी। प्रकाशित component minimums में ClickHouse के लिए 8 GiB और web तथा worker containers में से प्रत्येक के लिए 4 GiB शामिल हैं। उसी पृष्ठ पर Postgres के लिए 4 GiB, Redis के लिए 1.5 GiB और MinIO के लिए 4 GiB सूचीबद्ध है, इसलिए ये सभी छह मिलकर 25.5 GiB होते हैं, जो 16 GiB Compose अनुशंसा से अधिक है।

data retention सेट करने के बाद भी मेरा ClickHouse disk क्यों भरता रहता है?

Retention सेटिंग केवल Langfuse के अपने डेटा को कवर करती है। ClickHouse अलग से अपनी diagnostic tables trace_log, text_log, opentelemetry_span_log, metric_log, asynchronous_metric_log और latency_log लिखता है, और ये बिना किसी TTL के आते हैं। सबसे बड़ी table देखने के लिए table के अनुसार group की गई query system.parts चलाएं, फिर /etc/clickhouse-server/config.d/ के अंतर्गत एक file में remove="1" entry के साथ unused tables को disable करें, ClickHouse को restart करें, और पहले से उपयोग की गई जगह को पुनः प्राप्त करने के लिए मौजूदा tables को drop करें।

Langfuse में न्यूनतम data retention अवधि क्या है?

तीन दिन। Retention प्रत्येक project के लिए project settings में, या Organization Management API के माध्यम से सेट की जाती है, और एक nightly job ClickHouse और blob storage दोनों से उस window से पुराने traces, observations, scores और media assets को हटा देती है, सिवाय उन media के जो saved dataset items से जुड़े हैं। self-hosted instance पर यह एक Enterprise Edition feature है जिसके लिए license key की आवश्यकता होती है। इसके बिना, डेटा को अनिश्चित काल तक रखा जाता है। deletion को पूर्ववत (undo) नहीं किया जा सकता है, इसलिए यदि आपको उस window से परे history की आवश्यकता है तो पहले blob storage export कॉन्फ़िगर करें।

क्या मुझे Postgres और ClickHouse दोनों का बैकअप लेना होगा?

हाँ, क्योंकि वे अलग-अलग चीजें रखते हैं। Postgres में users, organisations, projects और API keys होते हैं, और ClickHouse में स्वयं trace data होता है। केवल Postgres को restore करने पर आपको एक ऐसा instance मिलेगा जिसमें आप login तो कर सकते हैं लेकिन उसमें कुछ भी नहीं होगा। MinIO bucket का भी बैकअप लें, क्योंकि इसमें वे raw events होते हैं जिन्हें Langfuse आने पर persist करता है, जो इस stack में source of truth के सबसे करीब है।

क्या मैं मौजूदा OpenTelemetry setup को self-hosted Langfuse पर point कर सकता हूँ?

हाँ। Langfuse v4 और इसके वर्तमान SDKs OpenTelemetry पर बने हैं, और Anthropic तथा OpenAI OTel instrumentations सीधे इसी पर export करते हैं। Python में, pip install langfuse opentelemetry-instrumentation-anthropic चलाएं, startup पर एक बार AnthropicInstrumentor().instrument() को call करें, और LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY तथा LANGFUSE_BASE_URL को अपने host पर सेट करें। missing dashboard की तलाश करने से पहले langfuse.auth_check() के साथ पुष्टि करें।

क्या यह लेख उपयोगी था?