Overview
Langfuse is an open-source LLM observability and evaluation platform — traces, prompt management, evaluations, and a playground. This template deploys the full self-hosted stack on Control Plane: the web app, a background worker, and its three datastores, with trace data landing in an object store you own. The instance is closed by default. Self-service registration is off, and the owner account is provisioned at boot from a secret you create — so a public endpoint does not hand a stranger an account.Architecture
- Langfuse Web — Next.js app serving the UI and public API on port
3000. Autoscales between 2 and 5 replicas on CPU. - Langfuse Worker — Background processor for trace ingestion, automated evaluations, and integrations.
- PostgreSQL — The postgres template as a subchart. Stores users, projects, API keys, prompts, datasets, and evaluation configs.
- Redis — BullMQ ingestion queue and the API key/prompt cache, on a persistent volume set.
- ClickHouse — Columnar store for all traces, observations, and scores; powers the dashboards. Its data parts live in the object store, so the volume set holds local metadata only.
- Object Storage — AWS S3 or Google Cloud Storage. One bucket serves both ClickHouse (
clickhouse/) and Langfuse (events/,media/).
What Gets Created
- Stateful Langfuse Web Workload — The UI and public API on port
3000, autoscaling betweenlangfuse.web.minReplicasandlangfuse.web.maxReplicas. - Stateful Langfuse Worker Workload — One replica by default, processing the ingestion queue.
- Stateful ClickHouse Workload — Single node on ports
8123(HTTP) and9000(native). - Stateful Redis Workload — Single node on port
6379. - PostgreSQL Subchart Resources — A stateful PostgreSQL workload with its own volume set, identity, policy, and credentials secret.
- Volume Sets —
{release}-langfuse-redis-vsand{release}-langfuse-clickhouse-vs, plus the PostgreSQL subchart’s, at 10 GiB each by default. - Secrets — A dictionary secret holding the bundled datastore credentials, and two secrets holding the ClickHouse startup and storage configuration. The auth secret and the GCS credentials secret are prerequisite secrets you create yourself; the chart references them by name and never creates, modifies, or deletes them.
- Identity & Policy — An identity for the Langfuse workloads and a policy granting it
revealon exactly those secrets. On the AWS path the identity is also linked to your cloud account with your bucket-scoped IAM policy.
This template does not create a GVC. You must deploy it into an existing GVC.
Prerequisites
Two things must be in place before you install: the auth secret, and the object storage the traces are written to.Auth Secret
A dictionary secret holding five keys must exist before you install. It carries the key material that signs sessions and encrypts stored provider credentials, plus the login for the account the template provisions — none of which should ever sit invalues.yaml, where it would land in the Helm release for the life of the install.
Create it with your own values, then set langfuse.auth.secretName to the name you used:
Use three different random values for
nextAuthSecret, encryptionKey, and salt, and keep a copy of encryptionKey somewhere safe outside Control Plane.
Object Storage
Both ClickHouse and Langfuse use one bucket with separate key prefixes. Pick one provider and complete its setup before installing.AWS S3
1
Create a bucket
Create an S3 bucket. Set
objectStore.aws.bucket to its name and objectStore.aws.region to its region.2
Set up a Cloud Account
If you do not have one, create a Cloud Account for your AWS account. Set
objectStore.aws.cloudAccountName to its name — the workloads then reach the bucket keylessly, with no stored credentials.3
Create a bucket-scoped IAM policy
Create an IAM policy with the following JSON (replace
YOUR_BUCKET_NAME) and set objectStore.aws.policyName to its name:Google Cloud Storage
Langfuse and ClickHouse reach GCS over its S3-compatible endpoint, so this path uses an HMAC key pair rather than a Control Plane Cloud Account.
1
Create a bucket
Create a GCS bucket. Set
objectStore.gcp.bucket to its name.2
Create a service account and an HMAC key
Grant the service account In the console the same key lives under Cloud Storage → Settings → Interoperability → Create a key for a service account.
roles/storage.objectAdmin on the bucket, then create an HMAC key for it:3
Put the pair in a dictionary secret
The HMAC pair never passes through Helm values. Create a second dictionary secret and set
objectStore.gcp.credentialsSecretName to its name:UI
Browse, install, and manage templates visually
CLI
Manage templates from your terminal
Terraform
Declare templates in your Terraform configurations
Pulumi
Declare templates in your Pulumi programs
Configuration
The defaultvalues.yaml for this template:
Auth and the Admin Account
All five auth keys are read from the dictionary secret named bylangfuse.auth.secretName (see Auth Secret). The web workload reads all five; the worker reads only nextAuthSecret, encryptionKey, and salt.
langfuse.auth.disableSignup defaults to true, and the admin account is the only way in. Langfuse OSS has no first-user exemption — its AUTH_DISABLE_SIGNUP flag is enforced in both the signup handler and the NextAuth adapter, so closing registration on its own would leave an instance nobody could enter. The template therefore provisions the owner headlessly at boot from adminEmail and adminPassword, using Langfuse’s headless initialization. The bootstrap runs on every boot and leaves existing rows alone.
With the defaults in place:
- Signing in with
adminEmail/adminPasswordyields a session whose role isOWNER, and that account can create projects immediately. - Anyone else hitting
/api/auth/signupis refused withHTTP 422and{"message":"Sign up is disabled."}, and the sign-up page reports registration as disabled.
langfuse.auth.organizationName names the organization created for that owner. It is required and defaults to Langfuse.
Set langfuse.auth.disableSignup: false if you want self-service registration instead — anyone who can reach the UI can then create their own account and their own organization.
Public URL and Session Cookies
langfuse.publicUrl sets the origin the browser uses. Leave it empty and the template derives it: with publicAccess.enabled: true the web workload uses its own canonical https://*.cpln.app endpoint, and with public access off it falls back to the internal cpln.local address. Set it only for a custom domain, and include the scheme — a scheme-less value is rejected at render.
This value does more than build redirect links:
- The session cookie’s
Secureflag is derived from it. NextAuth turns onuseSecureCookiesand the__Secure-cookie prefix only when the URL starts withhttps:. On a public instance the derived HTTPS endpoint gives you__Secure-next-auth.session-tokenwithsecure=true. On a private instance the origin genuinely is plain HTTP, so the flag is correctly left off — aSecurecookie there would simply be dropped. - NextAuth’s own server-rendered pages build their form actions from it. The sign-out confirmation page at
/api/auth/signoutis one of them; a URL the browser cannot reach leaves that page’s button dead-ending on a browser error interstitial.
targetPath parameter rather than following a redirect header.
If you serve Langfuse on a custom domain, set
langfuse.publicUrl to that domain. The derived value is the platform-assigned canonical endpoint, which is not your domain.Access
publicAccess.enabled— Serve the UI and public API on the canonical*.cpln.appHTTPS endpoint (defaulttrue). Everything behind it is gated by Langfuse’s own login. Set tofalseand external requests are refused at the edge with403, while in-GVC callers still reach it perinternalAccess.internalAccess.type— Internal firewall scope of the web workload:
Firewall changes take up to a couple of minutes to propagate — measured transitions landed between 51 and 105 seconds. The old behavior keeps serving for the first minute or so after the upgrade returns, so re-poll instead of trusting the first response.
Object Store
Both ClickHouse and Langfuse share one bucket with separate key prefixes:
Set
objectStore.provider to aws or gcp and fill in the matching block; the other block is ignored, and nothing from it is rendered into the workloads. On the GCS path the HMAC pair is read from your credentials secret at runtime — the ClickHouse storage configuration references it by environment variable, so the key never appears in a rendered file.
Datastores
The three datastore passwords are internal plumbing: PostgreSQL, Redis, and ClickHouse serve Langfuse only and are unreachable from outside the GVC. They are still used exactly as written, so change all three before installing — they ship as obviouschange-me- placeholders:
postgres.credentials.passwordredis.auth.passwordclickhouse.config.password
postgres.config.credentialsSecretName. Secret names are organization-wide, so a second release left on the default name is refused at install and creates nothing — the first release is unaffected.
Each datastore’s volumeset.capacity is its initial size in GiB, with 10 the minimum.
Accessing Langfuse
A fresh install takes roughly two minutes to become usable: the datastores converge in about 45 seconds and the web tier follows.
Sending Traces
Create a project in the UI, generate a key pair under Settings → API Keys, then post to the public API:events/ prefix in your bucket, the worker picks it up off the Redis queue and writes it into ClickHouse, and the trace becomes queryable at GET /api/public/traces/{id} a few seconds later. For application instrumentation, use a Langfuse SDK instead of the raw API.
LLM Connections
The playground and LLM-as-a-Judge evaluations need provider API keys, added in the UI under Settings → LLM Connections. Those keys are stored in PostgreSQL encrypted with yourencryptionKey — which is why that key can never be rotated, and why it belongs in a secret rather than in your Helm release.
Upgrades
Upgrading From 1.0.0 or 1.0.1
Each one fails with a message naming its own replacement, for example:
1
Back up PostgreSQL
It holds your users, projects, API keys, prompts, and datasets. Take a volume set snapshot before you change anything.
2
Create the prerequisite secrets
Create the auth secret with all five keys (see Auth Secret), and on the GCS path the credentials secret as well.
adminEmail and adminPassword have no 1.0.x equivalent — they are new, and they provision the owner account.3
Remove the six keys and name the secrets
Delete every removed key from your values file, then set
langfuse.auth.secretName and, on GCS, objectStore.gcp.credentialsSecretName. Replace langfuse.firewall.inboundAllowCIDR with publicAccess.enabled and internalAccess.type.4
Upgrade
Run the upgrade, then confirm both
{release}-langfuse-web and {release}-langfuse-worker reach ready with cpln workload get-deployments.The First Upgrade Interrupts Trace Ingestion
Plan the first upgrade as a short ingestion outage, and check that traces are landing again afterwards rather than relying on the health endpoint.Changes That Wait for the Web Tier
Anything carried in the web container’s environment —langfuse.auth.disableSignup, langfuse.publicUrl, values from the auth secret — only takes effect as replicas roll. Measured windows on a two-replica tier: about 93 seconds to close signup, about 152 seconds to open it, and 120 to 141 seconds for a publicUrl change to reach both replicas. During a rollout the old replicas still serve the old behavior.
Backups
PostgreSQL
Holds all configuration: users, projects, API keys, prompts, datasets, and evaluation configs. Most important to back up. Enable snapshot policies on the PostgreSQL volume set — snapshots capture the full disk state and restore quickly.
ClickHouse
Trace data parts live in your own S3 or GCS bucket and are as durable as that bucket; the volume set holds local metadata only. Back up the bucket according to your provider’s practice.
Redis
Holds only the transient BullMQ ingestion queue and short-lived cache. No backup required.
Important Notes
- Create the auth secret before installing. A missing one does not fail the install — Helm reports success and the web and worker workloads then sit waiting on a secret that does not exist.
encryptionKeycannot be rotated. It encrypts every provider key stored under Settings → LLM Connections; changing it makes all of them unreadable. Keep a copy outside Control Plane, and never delete the secret.- Self-service signup is closed by default and the owner account comes from your secret. Record
adminPassword— it is displayed nowhere, and with signup closed there is no other way to create an account. - Change the three datastore passwords before installing. They ship as
change-me-placeholders and are used exactly as written. - Prerequisite secrets are not owned by the release. The auth and GCS credentials secrets survive
helm uninstalland must be deleted by hand if you no longer want them. - Web and worker log database connection errors for the first ~40 seconds of a cold install (
Can't reach database server,Applying database migrations failed) while PostgreSQL is still starting. The retry succeeds and migrations then apply; this is startup ordering, not a fault. - Access-knob changes take up to a couple of minutes to propagate, and rollout-driven changes such as
disableSignuptake a minute or two more. Re-test before concluding a knob did not work. - Uninstall deletes the volume sets, including PostgreSQL’s. Trace data already in your bucket is untouched, but users, projects, and API keys are not.
External References
Langfuse Documentation
Full Langfuse product documentation
Langfuse SDKs
Python, TypeScript, and other language SDK guides
Self-Hosting Guide
Self-hosting architecture and operations
Configuration Reference
Every Langfuse environment variable and what it controls
Headless Initialization
How the bootstrapped owner account is provisioned
Create a Cloud Account
Set up a Control Plane Cloud Account for keyless S3 access