How do I migrate from Langfuse to Rius?
Rius takes OpenTelemetry traces, so migrating from Langfuse is usually a routing change: re-point your OTLP exporter, or swap the Langfuse SDK for the Rius SDK. You can send the same traces to both backends while you evaluate, then cut over when Rius shows what you need. The full walkthrough is in Migrate from Langfuse, and the wider picture is in Migrate to Rius.
How it works
Your path depends on how your code is instrumented today.
- You send OTLP to Langfuse (OpenLLMetry, OpenInference, a vanilla OpenTelemetry SDK, or a Collector pointing at Langfuse’s OTLP endpoint). Your instrumentation stays. You change only the endpoint and the auth header. Rius reads the OpenTelemetry GenAI conventions, OpenInference conventions and OpenLLMetry attributes, so models, tokens and cost show up without code changes.
- You use the Langfuse SDK (
@observe,start_as_current_observation). You swap it for the Rius SDK, whose shape is close: a decorator, context managers for spans and generations, and a session scope. The swap is mostly mechanical.
The concepts map like this:
- A Langfuse trace is a Rius trace, with the same tree view.
- A Langfuse project becomes an agent, named by
service.name. Give each environment, such as production and staging, its own workspace and API key. - Observations map to spans, and generations map to generations: a span with a model, tokens and cost.
- Sessions stay sessions.
- A Langfuse
user_idbecomesuser.id. Set it withrius.user(...), or on every span of the trace, the root included. Each user’s cost, failures and traces appear on the Users page.
Auth changes too. Langfuse’s OTLP endpoint uses Basic auth built from a public and a secret key. Rius takes one API key as a Bearer token over OTLP/HTTP. There is no gRPC endpoint.
Set it up
-
Create a Rius workspace and an API key. A new organization starts on a trial of 30 days or 500,000 spans, whichever comes first, with no card.
-
Path A, keep your instrumentation. Re-point the exporter. Set
OTEL_SERVICE_NAMEif your setup does not already set a service name.
# Before: pointing at Langfuse
export OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <base64 public:secret>"
# After: pointing at Rius
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.eu.console.rius-glassflow.com"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer <your API key>"- Run both backends side by side. If you route through an OpenTelemetry Collector, add Rius as a second exporter. Use the
otlphttpexporter, because Rius takes OTLP over HTTP.
exporters:
otlphttp/langfuse:
endpoint: https://cloud.langfuse.com/api/public/otel
headers:
authorization: Basic ${env:LANGFUSE_BASIC_AUTH}
otlphttp/rius:
endpoint: https://ingest.eu.console.rius-glassflow.com
encoding: proto
compression: gzip
headers:
authorization: Bearer ${env:RIUS_API_KEY}
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/langfuse, otlphttp/rius]- Path B, swap the SDK. Replace
Langfuse(...)withrius.init(api_key="ri_...", service_name="my-agent"), and@observe()with@rius.observe. Usespan.set_input(...)andspan.set_output(...)forspan.update(...),gen.set_usage(input_tokens=i, output_tokens=o)for usage, andwith rius.session(session_id):forpropagate_attributes(session_id=...). The decorator has no generation mode, so userius.start_as_current_generation(...)inside the function. A generation looks like this:
# Rius
with rius.start_as_current_generation(
"chat gpt-4o", model="gpt-4o", provider="openai",
) as gen:
gen.set_input(messages)
response = client.chat.completions.create(model="gpt-4o", messages=messages)
gen.set_output(response.choices[0].message.content)
gen.set_usage(
input_tokens=response.usage.prompt_tokens,
output_tokens=response.usage.completion_tokens,
)-
Check the integrations list before porting generation blocks. If you call OpenAI, Anthropic or another supported library,
rius.init()instruments it and you may not need manual generations. -
Check afterwards.
- Verify one traced request end to end: the trace tree looks right, the generation shows model and token counts, and cost appears. If analytics fields are empty, check which attribute convention your instrumentation uses.
- Compare both backends on real traffic for a few days: trace counts, token totals and cost.
- Move anything that reads from Langfuse, such as dashboards, alerts and internal tools, to its Rius equivalent, or leave it behind on purpose.
- Remove the Langfuse exporter or SDK, and revoke the Langfuse keys once nothing sends to them.
What you get
- Traces with the same tree view, plus sessions and users, from the first span you send.
- Token and cost analytics. Rius computes cost from the model and token counts, so you never send a cost value. Costs are list-price estimates in US dollars.
- With the Rius SDK, a view of which agent processes are alive and, with partial spans turned on, runs still in progress.
- An MCP server, so your coding agent can query your traces.
- A reversible migration. While both backends receive the same spans, you can go back until you revoke your Langfuse keys.
Existing traces stay in Langfuse, because Rius starts from the first span you send it.
When something else fits
- Rius has no scores, annotation queues, evals, datasets or prompt management. If you rely on them, keep that workflow in Langfuse or a dedicated eval tool, and send your traces to both with the side-by-side setup.
- Rius is a managed service only, so it cannot be self-hosted. If you need to run your observability backend yourself, Rius is not the fit.