Self-Host Langfuse to Trace Your AI Agents

Self-Host Langfuse to Trace Your AI Agents

Your agent worked fine in the demo. Then it hit production, and one afternoon a user asked it something ordinary and it burned through 40 tool calls, spent way too many tokens, and returned a confidently wrong answer. You open your logs and find a single line: "agent finished." Great. Which step went sideways? No idea.

This is the part nobody warns you about when you move from a chatbot to an agent. A plain LLM call is one request and one response, easy to log. An agent is a loop: it reasons, calls a tool, reads the result, reasons again, and maybe recurses into a sub-agent. When something breaks, "the LLM did a thing" is not a useful trace. You need the whole tree.

That is exactly what Langfuse gives you, and you can run the whole thing on your own server.

What Langfuse actually does

Langfuse is an open-source observability and tracing platform built for LLM apps and agents. Think of it as the equivalent of an APM, but instead of HTTP spans it understands prompts, completions, tool calls, token usage, and cost. Every agent run becomes a trace, and inside that trace you get a waterfall of nested spans: the top-level request, each LLM generation with its exact input and output, every tool call, and the latency and token cost of each one.

The features you will actually use day to day:

  • Tracing: full nested view of an agent run, down to individual tool calls and sub-agents.
  • Cost and latency: per-trace and per-step token counts and dollar cost, so you can find the step that blew the budget.
  • Prompt management: version your prompts and roll them out without a redeploy.
  • Evals and datasets: score outputs (manually or with an LLM judge) and replay them against saved test cases.

One honest note, because you will hear about it anyway: ClickHouse acquired Langfuse in January 2026. The good news is the core platform stayed MIT licensed and the self-hosted path is unchanged and actively maintained. You are not deploying something about to get paywalled out from under you.

Why self-host it

Your traces are some of the most sensitive data you have. They hold the exact prompts, the user inputs, the model outputs, and often chunks of your private documents pulled in by RAG. Sending all of that to a third-party SaaS is the kind of thing that makes your security team wince.

Self-hosting keeps every trace on infrastructure you control. Same feature set, none of the "where did our customer data just go" conversation.

Deploy it on Elestio

Langfuse v3 is not a single container. It is a small stack: a web app, a background worker for ingestion, Postgres for transactional data, ClickHouse for the high-volume trace and observation storage, Redis (or Valkey) for the ingestion queue, and an S3-compatible blob store for large payloads. Wiring all of that together by hand is a genuine afternoon of Docker Compose debugging.

On Elestio it is a one-click deploy that provisions and connects the full stack for you, with backups, updates, and TLS handled. Pricing starts at around $16/month for the VM. You can spin one up from the Langfuse on Elestio page.

Once it is running, grab your public and secret API keys from Settings > API Keys in the Langfuse UI. You will need both.

Instrument your agent

The fastest win is the drop-in OpenAI wrapper. Change one import and every call is traced automatically:

# pip install langfuse
# Set these in your environment, not in source:
#   LANGFUSE_PUBLIC_KEY   (pk-lf-...)
#   LANGFUSE_SECRET_KEY   (sk-lf-...)
#   LANGFUSE_HOST         (https://your-langfuse.vm.elestio.app)
from langfuse.openai import openai  # drop-in replacement, reads the env vars above

client = openai.OpenAI()
resp = client.chat.completions.create(
    model="gpt-5.6-sol",
    messages=[{"role": "user", "content": "Summarize today's incidents."}],
)
print(resp.choices[0].message.content)

That already logs the prompt, the completion, token usage, and cost. But an agent is more than one call, so wrap your own functions with the @observe decorator to build the tree:

from langfuse import observe
from langfuse.openai import openai

client = openai.OpenAI()

@observe()
def search_docs(query: str) -> str:
    # your real retrieval / MCP tool call goes here
    return "3 matching runbooks found"

@observe()
def run_agent(question: str) -> str:
    context = search_docs(question)
    resp = client.chat.completions.create(
        model="gpt-5.6-sol",
        messages=[
            {"role": "system", "content": f"Context: {context}"},
            {"role": "user", "content": question},
        ],
    )
    return resp.choices[0].message.content

run_agent("Why did last night's deploy roll back?")

Now search_docs shows up as a child span of run_agent, and the LLM call sits alongside it with its own cost and latency. That is the tree you were missing.

The Node/TypeScript SDK works the same way (npm install langfuse), and because Langfuse treats OpenTelemetry as first-class in v3, anything already emitting OTel spans, including most LangChain, LlamaIndex, and LiteLLM setups, can pipe straight in.

What you can finally see

Here is the difference in practice once traces are flowing:

Question you had Where Langfuse answers it
Which step blew the token budget? Per-span token and cost breakdown in the trace waterfall
Why is this agent slow? Latency per span, so you spot the tool call that takes 8 seconds
Which tool call actually failed? The exact span, its input, and its raw error output
Did my new prompt make things worse? Scores and evals compared across prompt versions

Troubleshooting

A few things that trip people up on a fresh self-hosted instance:

  • Traces are not showing up: ingestion is asynchronous. Traces flow through the worker and Redis queue before landing in ClickHouse, so a few seconds of lag is normal. If they never appear, check that the worker container is healthy and that your LANGFUSE_HOST points at the web app, not the worker.
  • Short scripts lose their last traces: the SDK batches events. Call langfuse.flush() (or let the @observe context exit cleanly) before a short-lived script exits, or you will drop the final batch.
  • ClickHouse eating memory: ClickHouse is the heavy component here. If the box feels starved under real traffic, resize the VM rather than starving it of RAM. This is the main reason to not run Langfuse on your smallest instance.
  • Blob store errors on large payloads: big prompts and outputs go to the S3-compatible store, not Postgres. If you see upload errors, confirm the storage credentials and bucket in the environment are correct.

Give your agents a black box recorder

Running an agent without tracing is like flying without instruments. It works right up until the moment it does not, and then you have nothing to go on. Langfuse turns every run into a readable story: what the agent thought, what it called, what it cost, and where it went wrong.

Spin one up on the Langfuse on Elestio page, point your SDK at it, and the next time your agent does something weird you will actually know why.

Thanks for reading ❤️ See you in the next one 👋