Langfuse v4: up to 165× faster · Read more
ResourcesMigrate from Arize Phoenix to Langfuse

Migrate from Arize Phoenix to Langfuse

This guide walks through migrating LLM observability from Arize Phoenix to Langfuse: tracing first (usually a same-day change), then datasets, experiments, and prompts.

Dynatrace announced a definitive agreement to acquire Arize (August 2026). Arize AX and Phoenix continue to operate as they do today. This guide is for teams that have already decided to move to Langfuse from Phoenix. If you are on Arize AX (spaces, arize.otel.register()), use Migrate from Arize AX. For a product comparison, see Langfuse vs. Arize AX / Phoenix.

TL;DR: Keep your OpenInference instrumentors. Replace phoenix.otel.register() with the Langfuse client, then call .instrument() on the same OpenInference package. Datasets, prompts, and evaluators are recreated via the Langfuse SDK/API; experiments re-run against the migrated datasets.

Want help cutting over? Talk to us about Cloud (EU, US, Japan; HIPAA on Pro+) or self-host. Core features match Cloud; enterprise governance modules need an Enterprise license when self-hosted.

Why teams migrate

Teams tend to evaluate a Phoenix-to-Langfuse move for a few recurring reasons:

  • Hosting and licensing model. Phoenix is source-available under the Elastic License 2.0 (as of August 2026). It is free to self-host on SQLite or PostgreSQL. Arize AX is a separate proprietary product; self-hosting AX is an Enterprise option. Langfuse's core is MIT-licensed, and self-hosting runs the same core product as Langfuse Cloud (EU, US, Japan; HIPAA on Pro+ with a signed BAA). Enterprise governance modules need an Enterprise license when self-hosted.
  • Organizations and tenancy. A Phoenix instance is one tenant: projects live inside it, with instance-wide admin / member / viewer roles and OAuth2/LDAP. Langfuse adds organizations above projects, plus project-level RBAC and Enterprise SSO. Phoenix documents org/space multi-tenancy and SAML as Arize AX features.
  • One product for the production loop. Phoenix covers tracing, evals, datasets, experiments, and prompts as a local-first app. Continuous evals on production traffic with alerting are an Arize AX feature. Langfuse keeps that loop on one MIT codebase: production traces feed datasets and experiments, managed evaluators can run on live traffic, and custom dashboards sit on the same data model.

Phoenix remains a capable tool for local, notebook, and self-hosted workflows, and if it serves your team well, there is no urgency to move. This guide is for teams that have decided to consolidate on Langfuse.

Concept mapping

Phoenix and Langfuse share most concepts, which keeps the mental migration small:

PhoenixLangfuseNotes
ProjectProjectLangfuse project is selected by API keys, not Phoenix project_name
Traces / spans (OpenInference)Traces / observationsSame OTel foundation; spans map to observations
DatasetsDatasetsVersioned example collections in both
ExperimentsExperiments / dataset runsRuns linked to dataset items and scores
Evals (LLM and code)Evaluators / LLM-as-a-judge + code evaluatorsBoth can score experiments and traces. AX productizes online evals with alerting
PlaygroundPlaygroundReplay and iterate on traced calls
Prompt ManagementPrompt ManagementPhoenix tags map to Langfuse labels

Step 1: Keep OpenInference, replace register()

Phoenix apps typically send traces with phoenix.otel.register(). Keep your OpenInference instrumentor (pip install langfuse). Replace register() with the Langfuse client, then call .instrument() on the same package. Application LLM calls stay unchanged.

# Before
from phoenix.otel import register

register(project_name="my-llm-app", auto_instrument=True)

# After: your existing OpenInference instrumentor (OpenAI shown)
from langfuse import get_client
from openinference.instrumentation.openai import OpenAIInstrumentor  # or AnthropicInstrumentor, LangChainInstrumentor, ...

get_client()
OpenAIInstrumentor().instrument()

Set LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, and LANGFUSE_BASE_URL (https://cloud.langfuse.com for EU; see Get started for US, Japan, HIPAA, and self-hosted). Confirm LLM spans show as generations with input, output, and token/cost.

Worked example: Tracing using the OpenInference SDK.

Phoenix tracing notes

Do not rely on a naive OTEL env-var swap while still calling register(). That helper prefers PHOENIX_COLLECTOR_ENDPOINT, Bearer auth, and on Python defaults toward gRPC. Langfuse accepts OTLP over HTTP with Basic auth.

In the application process, unset PHOENIX_COLLECTOR_ENDPOINT and PHOENIX_API_KEY, and check for a .env.phoenix file (Phoenix SDKs auto-load it). Restart after the change. Langfuse routes traces by API key, not Phoenix project_name. Call get_client() before .instrument(). In scripts and notebooks, call langfuse.flush() before exit.

If you already export with a generic OTLPSpanExporter or a collector (not register()), point that exporter at Langfuse:

OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel"  # EU
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic ${AUTH_STRING},x-langfuse-ingestion-version=4"
OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"

AUTH_STRING is echo -n "pk-lf-...:sk-lf-..." | base64. The traces path is /api/public/otel/v1/traces when the exporter needs a signal-specific URL.

Langfuse maps OpenTelemetry user.id / session.id (and langfuse.user.id / langfuse.session.id) when those attributes are set. HIPAA Cloud (https://hipaa.cloud.langfuse.com) is on Pro+ and needs a signed BAA before sending PHI.

If Phoenix showed an agent loop and tools around the LLM call, and Langfuse only shows the LLM call, Python SDK v4 is filtering those extra spans.

Step 2: Migrate datasets

Export dataset examples with the Phoenix client and recreate them with the Langfuse SDK. The shapes are close: each example's input, expected output, and metadata map directly.

from phoenix.client import Client as PhoenixClient
from langfuse import get_client

phoenix = PhoenixClient(base_url="http://localhost:6006")  # your Phoenix URL
source = phoenix.datasets.get_dataset(dataset="my-dataset")

langfuse = get_client()
langfuse.create_dataset(name="my-dataset")  # skip if the dataset already exists

for example in source:
    langfuse.create_dataset_item(
        id=example["id"],
        dataset_name="my-dataset",
        input=example["input"],
        expected_output=example["output"],
        metadata=example.get("metadata") or {},
    )
Dataset import notes

This copies the latest snapshot, not the full version history. Pass base_url so the Phoenix client does not need PHOENIX_COLLECTOR_ENDPOINT. Authenticated Phoenix instances still need PHOENIX_API_KEY (or api_key=) in this export process only.

A stable id (the Phoenix example id) makes a retry safe. Extra fields (tags, split labels, provenance) belong in metadata. Confirm item counts match after import.

Step 3: Recreate prompts and evaluators

Copy Phoenix chat messages into a Langfuse type="chat" prompt. Phoenix tags map to Langfuse labels (for example production).

langfuse.create_prompt(
    name="my-prompt",
    type="chat",
    prompt=[{"role": "user", "content": "Answer in one sentence: {{question}}"}],
    labels=["production"],
)
prompt = langfuse.get_prompt("my-prompt", label="production", type="chat")
prompt.compile(question="Why is the ocean salty?")
dataset = langfuse.get_dataset("my-dataset")

def task(*, item, **kwargs):
    messages = prompt.compile(**item.input)
    return run_your_app(messages)  # same path as production

dataset.run_experiment(name="after-migration", task=task)
Prompt template notes

Use type="text" only for a single string. Convert F-string placeholders ({question}) to Langfuse variables ({{question}}). Simple Mustache variables already match; sections and conditionals are not handled by Langfuse compile(). Put model, tools, and response-format settings in prompt config if your app will read them; saving config does not apply them automatically. Unresolved {{variables}} are left as-is. Do not import historical Phoenix experiment scores.

Step 4: Decide what to do with historical traces

Most teams cut over fresh: old traces stay queryable in Phoenix, and Langfuse becomes the system of record from cutover day. Bulk-importing historical traces is rarely worth it beyond a few showcase traces.

If you need a few historical traces

There is no turn-key history import. Re-run those requests, or send OpenTelemetry via the OTLP endpoint with x-langfuse-ingestion-version=4. Do not use the deprecated ingestion API.

Validation checklist

  • phoenix.otel.register() is gone; traces arrive as generations with token/cost data
  • User and session attribution works if you set user.id / session.id
  • Datasets migrated with item counts matching the source
  • Prompts resolve by name+label from application code
  • An experiment run exists on the migrated dataset
  • Team access set up (org/project roles, SSO if applicable)
  • Old exporter removed (or parallel window scheduled to end)

FAQ

Do I have to re-instrument my application?

No. Keep OpenInference. Replace phoenix.otel.register() with Langfuse get_client() and the same instrumentor's .instrument() call. Re-instrumenting with the Langfuse SDKs later is optional.

Does Langfuse support the frameworks Phoenix instrumented?

Langfuse has native integrations for the major frameworks (LangChain, LlamaIndex, OpenAI, Vercel AI SDK, and more) and accepts OpenInference instrumentation via OTLP, so framework coverage carries over rather than resetting. See the OpenInference cookbook.

Is Langfuse open source where Phoenix is?

Langfuse's core platform is MIT-licensed and self-hostable. Core features match Cloud; enterprise governance modules need an Enterprise license when self-hosted. Phoenix is licensed under the Elastic License 2.0 (source-available) as of August 2026. Check both licenses against your compliance requirements: ELv2 restricts offering the software as a managed service, which matters to some platform teams.

Can I evaluate old Phoenix traces in Langfuse?

Evaluators run on data in Langfuse, so historical evaluation requires those traces to exist in Langfuse first (see Step 4). The pragmatic path: start evaluators on new traffic at cutover and backfill only if a specific analysis demands it.

No traces, generic spans, or duplicate dataset items?
  • No traces: confirm keys and LANGFUSE_BASE_URL were set before get_client(), the instrumentor ran before application calls, .env.phoenix is unset in the app process, and short-lived processes call flush().
  • Generic spans: check that OpenInference is actually instrumenting the library you call. LLM spans should carry input.value / output.value and model attributes.
  • Duplicate dataset items: pass a stable id (the Phoenix example id works) on create_dataset_item().

Get help with the migration

Start on Langfuse Cloud or self-host. If you want a migration plan, talk to us.


Was this page helpful?

Last edited