Langfuse is the most widely deployed open-source LLM observability platform, and since January 2026 it has been part of ClickHouse. Its MIT-licensed core is genuinely self-hostable. What the quickstart will not tell you is which of its defaults are unsafe for a regulated deployment, which controls sit behind a commercial license, and which parts of the ingestion path see your raw prompts before any redaction runs.
This guide covers the gap between it boots and it passes review.
1. First, decide whether you should self-host at all
Self-hosting Langfuse is not a cost optimisation. It is a sovereignty decision, and it is worth being honest about that before you start, because the economics are not close.
A self-hosted deployment needs PostgreSQL, ClickHouse, Redis and S3-compatible object storage, plus the Langfuse web and worker containers. Public estimates put a medium-scale self-hosted deployment at roughly $3,000 to $4,000 per month once infrastructure and DevOps overhead are counted, against roughly $199 to $300 per month for equivalent managed cloud usage at mid-market volume. You are paying an order of magnitude more, and you are paying it in engineer-hours as much as in cloud spend.
That premium is rational in exactly one situation: when infrastructure-level isolation in your own account or VPC is mandatory rather than preferable. Data residency, contractual prohibitions on third-party processing, air-gapped environments, or a regulator who will ask where the prompts live.
The honest decision table
| Situation | Recommendation |
|---|---|
| Prompts contain no regulated data; you just want to save money | Use managed cloud. Self-hosting will cost you more and buy you nothing. |
| You want control over retention and redaction, but processing by a vendor is permitted | Managed cloud plus client-side masking and a retention policy. Revisit later. |
| Third-party processing is contractually or legally prohibited | Self-host. This guide is for you. |
| Air-gapped or classified environment | Self-host OSS specifically, not Enterprise. See section 3 on telemetry. |
| You need HIPAA or ISO 27001 evidence and have no platform team | Managed cloud is already certified. Self-hosting transfers that burden to you. |
2. The architecture you are actually signing up for
Langfuse v3 is not a single container. Understanding which component holds what matters enormously for a data protection review, because your reviewers will ask where personal data comes to rest, and the answer is four places.
| Component | What it holds | Review implications |
|---|---|---|
| PostgreSQL | Transactional data: users, organisations, projects, datasets, encrypted API keys | Version 12 or later, public schema, UTC timezone required. Connection pooling via DIRECT_URL. |
| ClickHouse | Traces, observations and scores: the analytical store, and the bulk of your volume | Largest storage cost driver. Typically around 10:1 compression. Holds prompt and completion content unless masked. |
| Redis | Cache and job queue | Absorbs ingestion peaks. Transient, but can hold event payloads in queue. |
| S3 / blob storage | Raw events, multi-modal inputs, batch exports, media | See section 3. This is the component most often missed in a PII review. |
| Web + Worker containers | API, UI, and the ingestion processing pipeline | Configuration is by environment variable and must be passed to all application containers. |
Dedicated implementations exist for Azure Blob Storage, Google Cloud Storage and OCI Object Storage, and MinIO if you host object storage yourself. Indicative minimum sizing is around 4 CPU cores, 16 GB RAM and 100 GB disk, which ClickHouse will drive upward with trace volume and retention window.
The ingestion path, in order
This sequence is the single most important thing in this document. Read it twice.
your app -> SDK / OTel exporter | | <-- CLIENT-SIDE MASKING happens here v Langfuse Web (ingestion API) | v S3 event bucket ...... RAW EVENT WRITTEN HERE | v Langfuse Worker | <-- SERVER-SIDE MASKING callback runs here v ClickHouse -> UI, API, exports
3. Five defaults that will fail your review
None of these are secrets, and none of them are bugs. They are all documented. They are simply not what a regulated deployment wants, and each one has cost somebody a re-review.
3.1 Data is retained forever by default
Trace data is stored indefinitely unless you configure otherwise. Retention is set per project, and data older than the window is purged nightly. A default deployment therefore accumulates prompts and completions without limit, which is difficult to defend under a storage-limitation principle. Set retention on day one, per project, before onboarding a single real trace.
3.2 Retention silently fails on versioned buckets
To use the retention feature self-hosted you must grant s3:DeleteObject to the Langfuse IAM role on all buckets. Langfuse only issues delete statements against the API. If you use versioned buckets, and regulated environments very often mandate versioning, then delete markers and non-current versions must be removed manually or by a lifecycle rule. Otherwise your retention policy is cosmetic: the UI shows the data gone, and the objects are still there.
3.3 Server-side masking requires a commercial licence
Langfuse offers two complementary masking approaches. Client-side masking is in the OSS SDKs. Server-side ingestion masking, the centralised callback that lets an administrator enforce redaction across every client regardless of what application teams do, requires an Enterprise licence key to activate. If you are running pure MIT OSS, your only redaction control is in application code, which means redaction is only as good as your least careful team. Plan the licence, or plan a shared SDK wrapper that teams cannot bypass.
3.4 Enterprise self-hosted telemetry cannot be turned off
Self-hosted Langfuse sends a small amount of deployment telemetry, routed via PostHog Cloud. It is aggregated across all projects and does not include raw traces, prompts, observations, scores or dataset contents. For OSS you can opt out entirely:
TELEMETRY_ENABLED=false
For Langfuse Enterprise self-hosted, that same telemetry is used for licence compliance and cannot be disabled. In a genuinely air-gapped or egress-controlled environment this is a procurement conversation, not a config flag, and it produces the counterintuitive result that the OSS build is the more air-gappable one. If you need both centralised masking (3.3) and zero egress, raise it with Langfuse before you design the network.
3.5 Langfuse stores what you send it, as-is
There is no implicit PII detection. Langfuse stores the data as-is and expects you to redact via masking. Nothing in the platform will notice that your prompts contain patient identifiers. The responsibility sits entirely on the instrumentation side, which is why section 4 is the longest in this guide.
4. PII containment
Given the ingestion order in section 2, containment has to happen before the payload leaves your application process. Everything else is defence in depth.
4.1 Mask at export, not at the integration layer
The Python SDK offers two masking hooks. For new setups prefer mask_otel_spans: it runs at the export stage against raw OpenTelemetry span attributes, which means it also covers spans produced by third-party instrumentation you did not write. The older per-observation mask hook only sees input, output and metadata on observations the SDK itself created, and that gap is exactly where an auto-instrumented framework will leak a raw prompt.
In JS/TS the equivalent is a mask callback on LangfuseSpanProcessor, receiving the stringified JSON of the attribute value and returning the masked form.
4.2 Layer the controls
| Layer | Control | Stops |
|---|---|---|
| Application | SDK masking hook at export stage | PII reaching the wire at all. The only layer that satisfies never comes to rest. |
| Collector | OTel Collector redaction processor, or a Presidio sidecar before export | Anything the app layer missed, centrally, without touching application code. |
| Ingestion | Server-side masking callback on the Worker (Enterprise) | Leakage into ClickHouse and the UI. Does not protect the S3 event bucket. |
| Storage | Retention windows, lifecycle rules, KMS | Indefinite accumulation and undeleted object versions. |
| Access | SSO, SCIM, RBAC, audit logs | The wrong humans reading traces that legitimately contain sensitive content. |
A useful rule from OpenTelemetry practice: content capture should be opt-in, enabled in pre-production or controlled debugging only. In production, prefer external object storage with a pointer on the span over embedding large payloads. Keep failure classification low-cardinality via error.type and leave stack traces in the normal exception fields rather than in custom GenAI attributes.
4.3 Instrument to the standard, not to the vendor
OpenTelemetry graduated CNCF in May 2026, and the GenAI semantic conventions, covering LLM client spans, agent spans, events and metrics, are the schema every vendor is now mapping to, though the GenAI and MCP convention pages remain marked Development. Emit gen_ai.* attributes rather than bespoke ones and your traces stay portable across Langfuse, Datadog, Honeycomb or Grafana. Langfuse itself ships OpenTelemetry-based SDKs, so following the convention costs you nothing and preserves your exit. One caveat worth passing to your team: instrumentation must not report token usage metrics it cannot efficiently obtain. Do not emit fake precision to fill a dashboard.
5. Secrets, encryption and identity
5.1 The secrets baseline
Every self-hosted deployment needs these set to real values and passed to all application containers. The published examples use an all-zero encryption key; shipping that to production is the single most common self-host mistake.
NEXTAUTH_SECRET=... # openssl rand -base64 32 SALT=... # openssl rand -base64 32 ENCRYPTION_KEY=... # openssl rand -hex 32 (256-bit, hex) DATABASE_URL=postgresql://... # or DATABASE_HOST / _USERNAME / _PASSWORD / _NAME CLICKHOUSE_URL=http://clickhouse:8123 CLICKHOUSE_MIGRATION_URL=clickhouse://clickhouse:9000 REDIS_HOST=... REDIS_PORT=6379 REDIS_AUTH=... LANGFUSE_S3_EVENT_UPLOAD_BUCKET=... LANGFUSE_S3_EVENT_UPLOAD_REGION=... TELEMETRY_ENABLED=false # OSS only; see 3.4
ENCRYPTION_KEY provides application-level encryption on top of whatever your database does at rest. Rotate these through your secret manager, not your compose file, and never let them reach a container image layer.
5.2 Encryption in transit and at rest
Langfuse does not terminate HTTPS itself; that is handled at the infrastructure level, typically by terminating at the load balancer and forwarding to the container, which keeps certificate management inside a managed service. If your threat model does not permit cleartext on the internal hop, terminate at the container instead and accept the certificate-management overhead.
For object storage, Langfuse supports AWS KMS across all S3 interactions via environment variables. The IAM role needs kms:GenerateDataKey and kms:Decrypt on the key. Customer-managed keys are a genuine differentiator of self-hosting here: they are not available on Langfuse Cloud, which uses AWS-managed AES-256 via KMS with no BYOK, CMEK or HSM option. If a CMEK requirement is what is driving your self-host decision, this is the paragraph to send your security team.
5.3 Identity and audit
Configure SSO and disable email/password sign-up before the instance is reachable by anyone other than you. On a fresh deployment the first account to register becomes the administrator, which is a race you do not want to lose. Use headless initialisation to provision the first organisation and project deterministically rather than clicking through the UI. Layer SCIM, RBAC and audit logs on top; in a regulated environment the audit log is usually the artefact your reviewer actually asks to see, because traces legitimately contain sensitive content and the control that matters is who read them.
6. Pre-flight checklist
Work through this before the first real trace is ingested. Retrofitting redaction after production data has landed means a purge, not a config change.
- Client-side masking implemented at the export stage (mask_otel_spans in Python, LangfuseSpanProcessor mask in JS/TS), not only at the integration layer
- Masking function benchmarked; no async I/O, no network calls, no unbounded retries on the export path
- Third-party auto-instrumentation verified as covered by the mask, not just first-party spans
- Prompt and completion content capture disabled or sampled in production; large payloads stored externally with a span pointer
- Collector-level redaction configured as a second layer independent of application teams
- Retention window configured per project; no project left on the indefinite default
- s3:DeleteObject granted to the Langfuse IAM role on all buckets
- Bucket versioning reviewed; lifecycle rule in place to expire delete markers and non-current versions
- Deletion verified at the bucket level, not in the Langfuse UI
- KMS configured with kms:GenerateDataKey and kms:Decrypt on the Langfuse role
- NEXTAUTH_SECRET, SALT and ENCRYPTION_KEY generated fresh; no example or all-zero values anywhere
- Secrets injected from a secret manager, absent from image layers and compose files
- PostgreSQL 12+, public schema, UTC timezone; pooling via DIRECT_URL if used
- TLS terminated per your threat model; internal hop reviewed explicitly
- Egress policy decided: TELEMETRY_ENABLED=false on OSS, or the Enterprise licence-compliance telemetry accepted in writing
- Backups configured and a restore actually rehearsed across all four datastores
- SSO configured and email/password sign-up disabled before the instance is publicly reachable
- First admin account provisioned via headless initialisation, not by racing the registration form
- RBAC roles mapped to real job functions; audit logging enabled and shipped off-box
7. What this guide does not solve
Hardening the platform tells you that your traces are safe. It does not tell you whether your agents work.
A hardened Langfuse will faithfully record a system with no eval coverage, no regression suite, swallowed exceptions around tool calls, and the same prompt duplicated across six files with no version history. Observability shows you the failure after it reaches a user. It is not the same thing as knowing your agent still does what it did last month.
The static-analysis tooling that exists for agent code is almost entirely security-oriented: taint tracking, prompt-injection surfaces, MCP configuration auditing, mapped to the OWASP Agentic Top 10. That work is valuable and you should run it. But it answers can this be attacked. It does not answer will this keep working, and would anyone notice if it stopped. That second question is where most production agent pain actually lives, and it is not a platform problem. It is an engineering-practice problem.
Who operates this?
Langfuse is explicit that it does not operate installations on customer infrastructure, even at the Enterprise tier. Somebody has to. If that somebody does not exist on your team yet, that is the work we do.
Engineer in Residence embeds with teams shipping AI systems into regulated environments. We deploy and harden self-hosted observability inside your VPC, instrument your codebase to the OpenTelemetry GenAI conventions, and then stay to own the thing that actually decays: your eval suite.
About this document. Compiled from Langfuse public documentation, OpenTelemetry GenAI semantic convention guidance, and field experience deploying LLM observability in regulated environments. Accurate as of July 2026 against Langfuse v3. Langfuse is MIT-licensed with certain peripheral features under a commercial licence, and has been part of ClickHouse since January 2026; features, licensing boundaries and defaults change quickly, so verify against current documentation before you deploy. This guide is not affiliated with or endorsed by Langfuse or ClickHouse. Nothing here is legal advice; your compliance obligations are yours to determine with counsel.