Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Langfuse production readiness checklist and verification. Use when preparing to deploy Langfuse to production, validating production configuration, or auditing existing setup. Trigger with phrases like "langfuse production", "langfuse prod ready", "deploy langfuse", "langfuse checklist", "langfuse go live".
.claude/skills/jeremylongshore-langfuse-prod-checklist/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-21 | ✗→✓ | ▲ Improved | 48% | 0% |
| case-01 | ✗→✓ | ▲ Improved | -3% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -22% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 61% | 0% |
Comprehensive checklist for deploying Langfuse observability to production with verified configuration, error handling, graceful shutdown, monitoring, and a pre-deployment verification script.
typescript// v4+ Production Config import { LangfuseSpanProcessor } from "@langfuse/otel"; import { NodeSDK } from "@opentelemetry/sdk-node"; const processor = new LangfuseSpanProcessor({ exportIntervalMillis: 5000, // Flush every 5s maxExportBatchSize: 50, // Batch size maxQueueSize: 2048, // Buffer limit }); const sdk = new NodeSDK({ spanProcessors: [processor] }); sdk.start(); // Graceful shutdown on all signals for (const signal of ["SIGTERM", "SIGINT", "SIGUSR2"]) { process.on(signal, async () => { await sdk.shutdown(); process.exit(0); }); }
typescript// v3 Legacy Production Config import { Langfuse } from "langfuse"; const langfuse = new Langfuse({ flushAt: 25, // Balance between latency and efficiency flushInterval: 5000, // 5 second flush interval requestTimeout: 15000, // 15s timeout enabled: true, // Explicitly enable }); process.on("beforeExit", () => langfuse.shutdownAsync()); process.on("SIGTERM", () => langfuse.shutdownAsync().then(() => process.exit(0)));
typescriptimport { observe, updateActiveObservation, startActiveObservation } from "@langfuse/tracing"; // Wrap all traced operations with error safety const tracedEndpoint = observe({ name: "api-endpoint" }, async (req: Request) => { try { updateActiveObservation({ input: { path: req.url, method: req.method }, metadata: { userId: req.userId }, }); const result = await processRequest(req); updateActiveObservation({ output: { status: 200 } }); return result; } catch (error) { // Log error to trace -- don't let tracing error mask app error try { updateActiveObservation({ output: { error: String(error) }, metadata: { level: "ERROR" }, }); } catch { // Tracing failure must never break the app } throw error; } });
typescript// scripts/verify-langfuse-prod.ts import { LangfuseClient } from "@langfuse/client"; import { startActiveObservation, updateActiveObservation } from "@langfuse/tracing"; async function verify() { const checks: Array<{ name: string; pass: boolean; detail: string }> = []; // 1. Environment variables const requiredVars = ["LANGFUSE_PUBLIC_KEY", "LANGFUSE_SECRET_KEY"]; for (const v of requiredVars) { checks.push({ name: `Env: ${v}`, pass: !!process.env[v], detail: process.env[v] ? `SET (${process.env[v]!.slice(0, 10)}...)` : "MISSING", }); } // 2. Key validation const pk = process.env.LANGFUSE_PUBLIC_KEY || ""; const sk = process.env.LANGFUSE_SECRET_KEY || ""; checks.push({ name: "Key format", pass: pk.startsWith("pk-lf-") && sk.startsWith("sk-lf-"), detail: `Public: ${pk.startsWith("pk-lf-")}, Secret: ${sk.startsWith("sk-lf-")}`, }); // 3. API connectivity try { const langfuse = new LangfuseClient(); // Try fetching prompts as a connectivity test await langfuse.prompt.get("__health-check__").catch(() => {}); checks.push({ name: "API connectivity", pass: true, detail: "Connected" }); } catch (error) { checks.push({ name: "API connectivity", pass: false, detail: String(error) }); } // 4. Trace creation try { await startActiveObservation("prod-verify", async () => { updateActiveObservation({ input: { test: true }, output: { verified: true }, metadata: { verification: "pre-deploy" }, }); }); checks.push({ name: "Trace creation", pass: true, detail: "Trace created" }); } catch (error) { checks.push({ name: "Trace creation", pass: false, detail: String(error) }); } // Report console.log("\n=== Langfuse Production Verification ===\n"); let allPassed = true; for (const check of checks) { const icon = check.pass ? "PASS" : "FAIL"; console.log(` [${icon}] ${check.name}: ${check.detail}`); if (!check.pass) allPassed = false; } console.log(`\n${allPassed ? "All checks passed." : "SOME CHECKS FAILED."}\n`); if (!allPassed) process.exit(1); } verify();
pk-lf- / sk-lf-)flushAt: 25-50)flushInterval: 5000)requestTimeout: 15000)try/finally (v3) or use observe/startActiveObservation (v4+)| Issue | Cause | Solution | |-------|-------|----------| | Missing traces in prod | No flush on exit | Add shutdown handler for SIGTERM | | Memory growth | Client created per request | Use singleton pattern | | High latency | Small batches | Increase flushAt to 25-50 | | Lost traces on deploy | No graceful shutdown | Add SIGTERM handler with sdk.shutdown() |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-21 | fail→pass | 27,144 | 29,664 | +9% | 1 | 1 | 0% | 3,652 | 5,404 | +48% | 0 | 0 | — |
case-01 | fail→pass | 29,990 | 20,079 | -33% | 1 | 1 | 0% | 4,902 | 4,761 | -3% | 0 | 0 | — |
case-02 | fail→fail | 23,607 | 21,648 | -8% | 1 | 1 | 0% | 3,860 | 5,333 | +38% | 0 | 0 | — |
case-03 | fail→pass | 45,121 | 25,590 | -43% | 1 | 1 | 0% | 7,122 | 5,558 | -22% | 0 | 0 | — |
case-04 | pass→pass | 20,574 | 21,371 | +4% | 1 | 1 | 0% | 2,738 | 4,718 | +72% | 0 | 0 | — |
case-05 | pass→pass | 20,539 | 21,471 | +5% | 1 | 1 | 0% | 2,860 | 5,106 | +79% | 0 | 0 | — |
case-06 | pass→pass | 20,264 | 23,050 | +14% | 1 | 1 | 0% | 2,929 | 5,070 | +73% | 0 | 0 | — |
case-07 | fail→fail | 41,096 | 15,812 | -62% | 1 | 1 | 0% | 2,585 | 3,674 | +42% | 0 | 0 | — |
case-08 | pass→pass | 19,905 | 19,510 | -2% | 1 | 1 | 0% | 2,841 | 4,554 | +60% | 0 | 0 | — |
case-09 | fail→pass | 21,293 | 14,606 | -31% | 1 | 1 | 0% | 2,305 | 3,456 | +50% | 0 | 0 | — |
case-10 | pass→pass | 23,014 | 14,700 | -36% | 1 | 1 | 0% | 2,619 | 3,616 | +38% | 0 | 0 | — |
case-11 | fail→fail | 24,703 | 11,662 | -53% | 1 | 1 | 0% | 2,812 | 3,842 | +37% | 0 | 0 | — |
case-12 | pass→pass | 14,764 | 8,129 | -45% | 1 | 1 | 0% | 1,485 | 3,005 | +102% | 0 | 0 | — |
case-13 | fail→fail | 27,005 | 14,163 | -48% | 1 | 1 | 0% | 1,267 | 3,500 | +176% | 0 | 0 | — |
case-14 | fail→fail | 15,486 | 7,150 | -54% | 1 | 1 | 0% | 2,212 | 2,986 | +35% | 0 | 0 | — |
case-15 | pass→pass | 16,659 | 14,779 | -11% | 1 | 1 | 0% | 2,555 | 3,980 | +56% | 0 | 0 | — |
case-16 | pass→pass | 17,424 | 16,090 | -8% | 1 | 1 | 0% | 1,790 | 4,152 | +132% | 0 | 0 | — |
case-17 | fail→pass | 10,655 | 7,603 | -29% | 1 | 1 | 0% | 1,835 | 2,952 | +61% | 0 | 0 | — |
case-18 | pass→pass | 27,594 | 20,122 | -27% | 1 | 1 | 0% | 3,061 | 4,504 | +47% | 0 | 0 | — |
case-19 | pass→pass | 32,188 | 21,246 | -34% | 1 | 1 | 0% | 3,249 | 4,107 | +26% | 0 | 0 | — |
case-20 | pass→pass | 24,765 | 25,796 | +4% | 1 | 1 | 0% | 3,150 | 4,782 | +52% | 0 | 0 | — |
case-22 | fail→fail | 24,243 | 27,802 | +15% | 1 | 1 | 0% | 3,385 | 5,160 | +52% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.