Langfuse Integration Guideยถ
Langfuse provides LLM observability for ContextForge, offering trace visualization, prompt management, evaluations, cost tracking, and analytics for AI-powered applications.
Why Langfuse?ยถ
Langfuse is purpose-built for LLM application observability:
- Trace visualization - End-to-end request traces with latency breakdown
- Prompt management - Version, test, and deploy prompts
- Evaluations - Score traces with custom or built-in evaluators
- Cost tracking - Token usage and cost analytics per model
- User analytics - Session-level and user-level aggregations
- Datasets - Create test datasets from production traces
- OpenTelemetry native - Receives traces via standard OTLP/HTTP
Quick Startยถ
Option 1: Docker Compose (Recommended)ยถ
# Configure the Langfuse project keys used by the gateway exporter
# LANGFUSE_PUBLIC_KEY=pk-lf-<set-a-unique-public-key>
# LANGFUSE_SECRET_KEY=sk-lf-<set-a-unique-secret-key>
# Start ContextForge with Langfuse
make langfuse-up
# Or manually:
docker compose -f docker-compose.yml \
-f docker-compose.with-langfuse.yml up -d
# View Langfuse UI
open http://localhost:3100
# Login with LANGFUSE_INIT_USER_EMAIL / LANGFUSE_INIT_USER_PASSWORD
# Defaults: admin@example.com / changeme unless you override them
# Verify that fresh MCP traffic lands in Langfuse
LANGFUSE_PUBLIC_KEY=pk-lf-contextforge \
LANGFUSE_SECRET_KEY=sk-lf-contextforge \
uv run pytest tests/e2e/test_langfuse_traces.py -q
Option 2: Standalone Langfuseยถ
If you already have a Langfuse instance running (self-hosted or cloud), configure ContextForge to send traces to it:
# Configure ContextForge OTEL to point at your Langfuse instance
export OTEL_ENABLE_OBSERVABILITY=true
export OTEL_TRACES_EXPORTER=otlp
export LANGFUSE_OTEL_ENDPOINT=http://your-langfuse:3000/api/public/otel/v1/traces
export OTEL_SERVICE_NAME=contextforge-gateway
# Preferred: configure Langfuse project keys and let the gateway derive OTLP auth
export LANGFUSE_PUBLIC_KEY=pk-lf-YOUR_PUBLIC_KEY
export LANGFUSE_SECRET_KEY=sk-lf-YOUR_SECRET_KEY
# Optional compatibility override if you already have a pre-encoded header value
# export LANGFUSE_OTEL_AUTH=$(echo -n "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY" | base64)
# export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $LANGFUSE_OTEL_AUTH"
# Start ContextForge
mcpgateway
Option 3: Langfuse Cloudยถ
For managed deployments, use Langfuse Cloud:
# Get API keys from your Langfuse Cloud project settings
export OTEL_ENABLE_OBSERVABILITY=true
export OTEL_TRACES_EXPORTER=otlp
export LANGFUSE_OTEL_ENDPOINT=https://cloud.langfuse.com/api/public/otel/v1/traces
export LANGFUSE_PUBLIC_KEY=pk-lf-YOUR_PUBLIC_KEY
export LANGFUSE_SECRET_KEY=sk-lf-YOUR_SECRET_KEY
export OTEL_SERVICE_NAME=contextforge-gateway
Architectureยถ
The integration uses OpenTelemetry (OTLP/HTTP) to send traces from ContextForge to Langfuse:
ContextForge Gateway
|
| OTLP/HTTP (protobuf)
v
Langfuse Web (port 3100)
|
+-- PostgreSQL (operational data, migrations)
+-- ClickHouse (OLAP trace analytics)
+-- MinIO (S3-compatible event/media storage)
+-- Redis (caching, queues)
|
v
Langfuse Worker (async processing)
OTLP Protocol
Langfuse only supports OTLP over HTTP (not gRPC). The Docker Compose overlay sets OTEL_EXPORTER_OTLP_PROTOCOL=http automatically.
Docker Compose Configurationยถ
The docker-compose.with-langfuse.yml overlay provides:
- langfuse-web - UI, API, and OTLP ingestion endpoint (port 3100)
- langfuse-worker - Async event processing (ClickHouse ingestion, evaluations)
- langfuse-db - Dedicated PostgreSQL instance (separate from ContextForge's)
- langfuse-clickhouse - OLAP analytics database
- langfuse-minio - S3-compatible object storage
- langfuse-cache - Dedicated Redis with auth
ContextForge only needs the Langfuse OTLP endpoint and project credentials. The self-hosted Langfuse database, cache, ClickHouse, and MinIO passwords are internal to the compose overlay and are not consumed by the gateway runtime.
The gateway is overridden to:
gateway:
environment:
- OTEL_ENABLE_OBSERVABILITY=true
- OTEL_TRACES_EXPORTER=otlp
- OTEL_EXPORTER_OTLP_PROTOCOL=http
- LANGFUSE_OTEL_ENDPOINT=http://langfuse-web:3000/api/public/otel/v1/traces
- LANGFUSE_PUBLIC_KEY=<your-project-public-key>
- LANGFUSE_SECRET_KEY=<your-project-secret-key>
- OTEL_SERVICE_NAME=contextforge-gateway
Separate Infrastructure
Langfuse uses its own PostgreSQL, Redis, ClickHouse, and MinIO instances. This avoids coupling with ContextForge's databases and allows independent lifecycle management.
What Gets Tracedยถ
ContextForge instruments these operations with OpenTelemetry spans:
| Operation | Span Name | Attributes |
|---|---|---|
| Tool invocation | tool.invoke | tool name, gateway, duration, status |
| Prompt rendering | prompt.render | prompt name, template vars |
| Resource fetch | resource.read | resource URI, MIME type |
| Gateway health check | gateway.health_check_batch | gateway count, check type |
Each span includes:
- Correlation ID for request tracing
- Service name and deployment environment
- Error details on failure (message, exception)
Makefile Targetsยถ
| Target | Description |
|---|---|
make langfuse-up | Start Langfuse + ContextForge with OTEL enabled |
make langfuse-down | Stop the Langfuse stack |
make langfuse-status | Show Langfuse service status |
make langfuse-logs | Tail Langfuse logs |
make langfuse-reset-data | Stop the Langfuse stack and remove only Langfuse data volumes |
make langfuse-clean-including-contextforge | Stop the combined stack and remove Langfuse and ContextForge volumes |
make langfuse-monitoring-up | Start Langfuse alongside Grafana/Prometheus/Tempo |
make langfuse-monitoring-down | Stop Langfuse + monitoring stack |
make langfuse-clean was removed because the name was ambiguous. Use one of the explicit targets above depending on whether you want to preserve ContextForge data.
Environment Variablesยถ
| Variable | Description | Default |
|---|---|---|
LANGFUSE_PORT | Host port for Langfuse UI | 3100 |
LANGFUSE_WORKER_PORT | Localhost-only Langfuse worker metrics port | 3130 |
LANGFUSE_OTEL_ENDPOINT | Langfuse OTLP/HTTP traces endpoint | local compose service |
LANGFUSE_PUBLIC_KEY | API public key | pk-lf-contextforge in the local compose overlay |
LANGFUSE_SECRET_KEY | API secret key | sk-lf-contextforge in the local compose overlay |
LANGFUSE_OTEL_AUTH | Optional base64-encoded pk:sk OTLP auth override | unset |
LANGFUSE_INIT_USER_EMAIL | Admin user email | admin@example.com |
LANGFUSE_INIT_USER_PASSWORD | Optional local overlay admin password override | changeme |
LANGFUSE_POSTGRES_PASSWORD | Optional local overlay DB password override | local compose default |
LANGFUSE_CLICKHOUSE_USER | ClickHouse username | clickhouse |
LANGFUSE_CLICKHOUSE_PASSWORD | Optional local overlay ClickHouse password override | local compose default |
LANGFUSE_MINIO_USER | MinIO access key | minio |
LANGFUSE_MINIO_PASSWORD | Optional local overlay MinIO password override | local compose default |
LANGFUSE_REDIS_AUTH | Optional local overlay Redis password override | local compose default |
LANGFUSE_NEXTAUTH_SECRET | Optional local overlay NextAuth secret override | local compose default |
LANGFUSE_SALT | Optional local overlay application salt override | local compose default |
LANGFUSE_ENCRYPTION_KEY | Optional local overlay encryption key override | local compose default |
OTEL_EMIT_LANGFUSE_ATTRIBUTES | Force-enable or disable Langfuse-specific span attributes | auto in normal runtime, true in local Langfuse compose overlay |
OTEL_CAPTURE_IDENTITY_ATTRIBUTES | Force-enable or disable user/team identity enrichment | auto in normal runtime, true in local Langfuse compose overlay |
OTEL_CAPTURE_INPUT_SPANS | Comma-separated allowlist of span names allowed to capture observation input payloads | empty in normal runtime, tool.invoke,prompt.render,llm.proxy,a2a.invoke in local Langfuse compose overlay |
OTEL_CAPTURE_OUTPUT_SPANS | Comma-separated allowlist of span names allowed to capture observation output payloads | empty |
OTEL_REDACT_FIELDS | Structured-field and free-text redaction keys used before export | password,secret,token,... |
OTEL_MAX_TRACE_PAYLOAD_SIZE | Max serialized input/output payload size in characters | 32768 |
Gateway vs Self-Hosted Langfuse Secrets
ContextForge itself only reads LANGFUSE_OTEL_ENDPOINT, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, and optionally LANGFUSE_OTEL_AUTH. The other LANGFUSE_* secrets in this table apply only when you run the local self-hosted Langfuse compose overlay.
Local Compose Defaults
The self-hosted Langfuse compose overlay uses local-only demo project keys when LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY are unset. This convenience is limited to the compose path; ContextForge code does not embed Langfuse credentials.
Payload Capture Defaults
ContextForge now uses allowlist-based payload capture. In normal runtime, OTEL_CAPTURE_INPUT_SPANS and OTEL_CAPTURE_OUTPUT_SPANS default to empty. The local Langfuse compose overlay enables a small dev-focused input allowlist for tool.invoke, prompt.render, llm.proxy, and a2a.invoke.
Using the Langfuse UIยถ
Viewing Tracesยถ
- Open http://localhost:3100
- Log in with the configured
LANGFUSE_INIT_USER_EMAIL/LANGFUSE_INIT_USER_PASSWORD - Navigate to Traces in the sidebar
- Each tool invocation appears as a trace with:
- Span name (e.g.,
tool.invoke) - Duration and latency
- Service attributes (deployment environment, namespace)
- Error details if the invocation failed
- Span name (e.g.,
Viewing Users, Sessions, and Generationsยถ
- Navigate to Users to group traces by
langfuse.user.id - Navigate to Sessions to inspect MCP session grouping via
langfuse.session.id - Navigate to Generations to inspect
llm.proxyandllm.chatspans withgen_ai.*token usage - Filter by tags such as
team:<team-id>,auth:jwt, andenv:production
Dashboard Workflowยถ
Use the Langfuse UI in this order when validating the gateway:
- Traces: confirm the request path and child spans
- Users: verify the request is attributed to the authenticated email
- Sessions: verify repeated MCP traffic groups under one session
- Generations: verify LLM spans include model and token usage
- Evaluations: score or annotate selected traces after reviewing outputs
Creating Evaluationsยถ
Langfuse supports scoring traces with evaluators:
- Navigate to Evaluations in the sidebar
- Create custom evaluators for response quality, latency, or cost
- Evaluators can run automatically on new traces
Prompt Managementยถ
Langfuse can version and manage prompts:
- Navigate to Prompts in the sidebar
- Create prompt templates with variables
- Track which prompt versions produce the best results
Combined with Monitoring Stackยถ
To run Langfuse alongside the full Grafana/Prometheus/Tempo monitoring stack:
This starts:
- Langfuse at
http://localhost:3100(LLM-specific analytics) - Grafana at
http://localhost:3000(infrastructure metrics and dashboards) - Prometheus at
http://localhost:9090(metrics collection) - Tempo at
http://localhost:3200(distributed tracing)
The gateway still exports traces to Langfuse in this mode. Tempo remains available for dashboards, metrics, and optional collector-based dual export.
If any of those host ports are already in use, override them before starting the stack. Supported compose-only overrides include LANGFUSE_PORT, LANGFUSE_WORKER_PORT, GRAFANA_PORT, LOKI_PORT, PROMETHEUS_PORT, TEMPO_PORT, TEMPO_OTLP_GRPC_PORT, TEMPO_OTLP_HTTP_PORT, PGADMIN_PORT, and REDIS_COMMANDER_PORT. LOKI_PORT defaults to 3101 so it does not collide with Langfuse on 3100.
Dual Trace Export
By default, OTEL traces go to Langfuse only. To send traces to both Langfuse and Tempo simultaneously, run an OpenTelemetry Collector with a fan-out pipeline. This repo now includes a sample collector config at infra/monitoring/otel-collector/collector.langfuse-tempo.yaml.
Example:
LANGFUSE_OTEL_AUTH=$(printf '%s' "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY" | base64)
docker run --rm \
--network mcp-context-forge_mcpnet \
-e LANGFUSE_OTEL_ENDPOINT=http://langfuse-web:3000/api/public/otel/v1/traces \
-e LANGFUSE_OTEL_AUTH="$LANGFUSE_OTEL_AUTH" \
-e TEMPO_OTLP_GRPC_ENDPOINT=tempo:4317 \
-v "$PWD/infra/monitoring/otel-collector/collector.langfuse-tempo.yaml:/etc/otelcol/config.yaml:ro" \
otel/opentelemetry-collector-contrib:0.123.0 \
--config=/etc/otelcol/config.yaml
Then point ContextForge at the collector instead of Langfuse directly:
Production Deploymentยถ
Security Hardeningยถ
-
Set the gateway-facing Langfuse credentials in
If you run the local self-hosted overlay and want to replace its internal service defaults, override the additional.env:LANGFUSE_*compose variables as well. -
Optionally precompute the OTEL auth header if you prefer to pass a base64 token instead of raw project keys:
WhenLANGFUSE_PUBLIC_KEYandLANGFUSE_SECRET_KEYare set, ContextForge can derive the OTLP Authorization header automatically. -
Configure OTEL payload controls for trace capture:
OTEL_EMIT_LANGFUSE_ATTRIBUTES=true OTEL_CAPTURE_IDENTITY_ATTRIBUTES=true OTEL_REDACT_FIELDS=password,secret,token,api_key,authorization,credential,auth_value,access_token,refresh_token,auth_token,client_secret,cookie,set-cookie,private_key OTEL_MAX_TRACE_PAYLOAD_SIZE=32768 OTEL_CAPTURE_INPUT_SPANS=tool.invoke,prompt.render,llm.proxy OTEL_CAPTURE_OUTPUT_SPANS=llm.proxy,llm.chatOTEL_CAPTURE_INPUT_SPANSandOTEL_CAPTURE_OUTPUT_SPANSare allowlists. Leave them empty to disable observation payload capture entirely. Structured payloads are redacted by field name, and all exported string values also pass through URL and free-text secret scrubbing. This covers common cases such astoken=..., signed URLs, and embeddedBearer/Basiccredentials, but it is still best practice to avoid placing secrets inside generic free-text fields such asquery,message, ordata. -
Enable TLS for the Langfuse endpoint in production.
If your OTLP endpoint uses a private CA, mount that CA into the gateway container or host trust store before enabling export. -
Verify startup credential enforcement. ContextForge now fails startup when a Langfuse OTLP endpoint is configured without a resolved
Authorizationheader. Supplying arbitraryOTEL_EXPORTER_OTLP_HEADERSis not enough; the final header set must contain valid Langfuse basic auth, whether derived fromLANGFUSE_PUBLIC_KEY/LANGFUSE_SECRET_KEY,LANGFUSE_OTEL_AUTH, or an explicitAuthorization=Basic ...OTLP header.
Kubernetesยถ
Langfuse provides a Helm chart for production Kubernetes deployments. Configure ContextForge's OTEL exporter to point at the Langfuse service endpoint.
Resource Requirementsยถ
| Service | CPU (min) | Memory (min) | Storage |
|---|---|---|---|
| langfuse-web | 0.5 core | 256 MB | - |
| langfuse-worker | 0.5 core | 256 MB | - |
| langfuse-db | 0.25 core | 256 MB | 1 GB+ |
| langfuse-clickhouse | 0.5 core | 512 MB | 5 GB+ |
| langfuse-minio | 0.25 core | 128 MB | 1 GB+ |
| langfuse-cache | 0.1 core | 64 MB | - |
Troubleshootingยถ
No Traces Appearingยถ
-
Check OTEL is enabled in the gateway:
VerifyOTEL_ENABLE_OBSERVABILITY=trueandOTEL_EXPORTER_OTLP_PROTOCOL=http. -
Check the gateway logs for export errors:
-
Check Langfuse health:
-
Verify traces via API:
Export Timeout Errorsยถ
If you see "Failed to export span batch due to timeout" in gateway logs:
- This is normal during startup while Langfuse initializes
- If persistent, check that
langfuse-webis healthy and reachable from the gateway container
S3 Upload Errorsยถ
If Langfuse logs show "Failed to upload JSON to S3":
- Verify MinIO is running:
docker compose exec langfuse-minio mc ls local/langfuse - Check that service names use hyphens (not underscores) - the AWS SDK rejects hostnames with underscores
Startup Fails With Langfuse Credential Errorsยถ
If the gateway exits during startup with a Langfuse credential error:
- Check
OTEL_EXPORTER_OTLP_HEADERS,LANGFUSE_OTEL_AUTH, or theLANGFUSE_PUBLIC_KEY/LANGFUSE_SECRET_KEYpair - Verify
LANGFUSE_OTEL_ENDPOINTpoints at/api/public/otel/v1/traces - Restart the gateway after fixing the missing or mismatched credentials
Next Stepsยถ
- OpenTelemetry Overview - All supported backends
- Phoenix Integration - Alternative AI observability backend
- Internal Observability - Built-in database-backed observability
- Langfuse Documentation - Full Langfuse documentation