Skip to main content

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 — the web app, a background worker, and the three datastores Langfuse requires — 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.
This template deploys into an existing GVC that you already have. It does not create, provision, or manage a GVC. Every workload below runs in each location of that GVC.

What Gets Created

One bucket serves both ClickHouse (prefix clickhouse/) and Langfuse (prefixes events/ and media/). The auth secret and, on GCS, the credentials secret are prerequisite secrets you create yourself: the chart references them by name and never creates, modifies, or deletes them.

Prerequisites

Two things must exist before you install: the auth secret, and the object storage that traces are written to.
1

Create the auth secret

A dictionary secret holding five keys. It carries the key material that signs sessions and encrypts stored provider credentials, plus the login for the owner account the template provisions at boot.
Every key is required and the names are exact:Use three different random values for nextAuthSecret, encryptionKey and salt, and keep a copy of encryptionKey outside Control Plane. Read the values back later with cpln secret reveal AUTH_SECRET_NAME -o yaml.Set langfuse.auth.secretName to this name.
2

Set up object storage

Pick one provider. ClickHouse and Langfuse share the bucket under separate prefixes.
  1. Create an S3 bucket. Set objectStore.aws.bucket to its name and objectStore.aws.region to its region.
  2. If you do not have one, create a Cloud Account for your AWS account and set objectStore.aws.cloudAccountName to its name. The workloads then reach the bucket keylessly.
  3. Create an IAM policy with the JSON below (replace YOUR_BUCKET_NAME) and set objectStore.aws.policyName to its name.
A missing prerequisite secret wedges the deployment instead of failing it. cpln helm install reports success, but the web and worker workloads never start and cpln logs returns nothing. The only place the missing secret is named is status.versions[].message:
Create the secret, and the deployment recovers on its own after a few minutes — or run cpln workload force-redeployment RELEASE_NAME-langfuse-web --gvc GVC_NAME to skip the wait.
Backing up the bundled PostgreSQL to a bucket is optional and needs its own bucket-scoped access; see Backing Up.

Installation

Install from the marketplace registry, naming your auth secret and bucket and replacing the three bundled datastore passwords, which the chart uses exactly as written:
On the GCS path replace the four objectStore.aws.* values with objectStore.provider=gcp, objectStore.gcp.bucket and objectStore.gcp.credentialsSecretName.

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

Object Store

Only the block matching objectStore.provider is rendered into the workloads; the other is ignored.

Langfuse

langfuse.publicUrl is the origin the browser uses: auth redirects, the pages NextAuth renders server-side, and whether the session cookie gets the Secure flag all derive from it. Leave it empty unless you serve Langfuse on a custom domain, and when you set it include the scheme — a value without one is rejected at render. With langfuse.auth.disableSignup: true (the default) the owner account from your auth secret is the only way in: Langfuse blocks both email/password sign-up and first-time SSO user creation, so the template provisions that account at boot using Langfuse’s headless initialization. Set it to false and anyone who can reach the UI can create an account and their own organization.

Access

These apply to the web tier only; the datastores are never reachable from outside the GVC. A firewall change takes up to a couple of minutes to take effect. With publicAccess.enabled: false the API stays reachable from other workloads in the GVC, but there is no browser path to the UI: cpln port-forward does not reach the Langfuse web tier, so keep public access on if you need to sign in.

PostgreSQL

Every knob of the postgres template is available under postgres.*, including scheduled backups — see Backing Up.

Redis

ClickHouse

The three datastore passwords are internal plumbing — PostgreSQL, Redis and ClickHouse serve Langfuse only — but they are used exactly as written, so change all three before installing.

Connecting

Verify the web tier is serving once cpln workload get-deployments RELEASE_NAME-langfuse-web --gvc GVC_NAME -o yaml reports it ready:

First Run

  1. Open the canonical endpoint in a browser and sign in with the adminEmail and adminPassword from your auth secret. The account is an OWNER of the organization named by langfuse.auth.organizationName, which already exists.
  2. Create a project, then generate a key pair under Settings → API Keys.
  3. Send a first trace with the public ingestion API, or point a Langfuse SDK at your endpoint with the same keys:
Ingestion is asynchronous: the web tier writes the raw event to the events/ prefix of your bucket, the worker takes it off the Redis queue and writes it into ClickHouse, and the trace appears in the UI a few seconds later.
  1. For the playground and LLM-as-a-judge evaluations, add provider API keys under Settings → LLM Connections. They are stored encrypted with your encryptionKey.

Operations

Backing Up

PostgreSQL is the one datastore that must be backed up: it holds users, projects, API keys, prompts, datasets and evaluation configs. ClickHouse keeps its data parts in your bucket and is as durable as that bucket — its volume set holds local metadata only. Redis holds only the transient ingestion queue and cache. Two paths cover PostgreSQL. A volume set snapshot captures the full disk state:
Or enable the bundled postgres template’s scheduled dump, which adds the RELEASE_NAME-postgres-backup cron workload and uploads a gzipped pg_dumpall to a bucket of your choice. It is off by default:
The bundled database’s identity picks up the bucket-scoped policy automatically. For the bucket, cloud account and IAM policy setup per provider — including the gcp and minio blocks — follow the postgres template’s backup prerequisites; the keys there live under postgres.backup.* here.
A zero-length backup object is a failed run, not a backup. If the dump cannot reach the database, the upload still writes a tiny empty gzip under a normal timestamped filename. Check the object size before relying on one.

Restoring a Backup

These paths have not been exercised against this template; verify them on a throwaway release before relying on them.
  • From a scheduled dump: the dump is a plain pg_dumpall output restored with psql against RELEASE_NAME-postgres.GVC_NAME.cpln.local:5432, using the postgres.credentials.username and postgres.credentials.password you installed with. The command sequence per provider is on the postgres template’s restore section; it must run from a client that can reach the bucket and the database, which means a workload inside the GVC.
  • From a volume set snapshot: cpln volumeset snapshot restore exists (check cpln volumeset snapshot restore --help) but has not been run against this template.
  • ClickHouse: trace data is in your bucket under clickhouse/. A fresh ClickHouse workload pointed at the same bucket is the recovery path, but it has not been verified; deleting the bucket loses all trace history regardless of snapshots.

Upgrading from 1.0.0 or 1.0.1

Version 1.1.0 removed six values keys and moved all auth key material into the prerequisite auth secret. A values file that still carries any of them fails at render, so the upgrade aborts before anything is applied and the live release is untouched — but it does not proceed until the keys are gone. There are no compatibility fallbacks. Each one fails with a message naming its replacement, for example:
1

Back up PostgreSQL

Take a volume set snapshot of RELEASE_NAME-pg-vs before changing anything (see Backing Up).
2

Create the prerequisite secrets

Create the auth secret with all five keys, and on the GCS path the credentials secret as well (see Prerequisites). adminEmail and adminPassword have no 1.0.x equivalent — they provision the owner account.
3

Remove the six keys and name the secrets

Delete every removed key from your values, set langfuse.auth.secretName and, on GCS, objectStore.gcp.credentialsSecretName. Replace langfuse.firewall.inboundAllowCIDR with publicAccess.enabled and internalAccess.type.
4

Upgrade and verify

Run the upgrade, then confirm RELEASE_NAME-langfuse-web and RELEASE_NAME-langfuse-worker reach ready with cpln workload get-deployments RELEASE_NAME-langfuse-web --gvc GVC_NAME -o yaml.
Treat the 1.0.x auth defaults as compromised. Versions 1.0.0 and 1.0.1 shipped working values for nextAuthSecret, encryptionKey and salt as defaults in a public repository, so any install that did not override them shared one publicly known set. Generate fresh values for all three. The trade-off on encryptionKey: carrying the old one forward keeps existing LLM connections readable but leaves them encrypted under a published key; a new one makes them undecryptable and they must be re-added under Settings → LLM Connections.

Upgrading from 1.1.0

Version 1.2.0 moved the bundled PostgreSQL to the postgres 3.4.1 template, which reads its credentials from a dictionary secret. Langfuse creates that secret for you from values, so there is no new prerequisite — only a rename: Carrying an old key fails the render with the postgres template’s message, which tells you to create a dictionary secret yourself. Ignore that advice here: move the three keys to postgres.credentials.* and you are done. postgres.config.credentialsSecretName is new and names the secret the chart creates; the default is fine for a single release. The auth, Redis and ClickHouse paths are unchanged.

Upgrading from 1.2.0

Version 1.2.1 removes the AWS managed policy aws::ReadOnlyAccess from the identity this template links to your AWS cloud account. That policy granted read access to every bucket in your AWS account and contained no write actions, so it was never carrying the object store itself — but it was silently supplying any read action your bucket-scoped policy omitted. The identity now carries cpln-connector and your own policy only. Before upgrading, update your IAM policy to the ten-action list in Prerequisites — 1.2.0 documented six actions. If it already matches, no action is needed. Nothing else changes.

Scaling and Availability

  • The web tier autoscales on CPU between langfuse.web.minReplicas and langfuse.web.maxReplicas. Keep the minimum at 2 or more for rolling deploys without downtime.
  • The worker runs a fixed langfuse.worker.replicas; raise it for higher ingestion throughput.
  • PostgreSQL, Redis and ClickHouse are single-instance on persistent volume sets. A node failure reschedules each and reattaches its volume, so the cost is minutes of downtime, not data loss.
  • The first helm upgrade after an install can restart the bundled datastores even when nothing changed; plan it as a short ingestion interruption and check that traces land again afterwards rather than trusting the health endpoint, which stays green throughout.
  • Settings carried in the web container’s environment — langfuse.auth.disableSignup, langfuse.publicUrl, any change to the auth secret — take effect only as the web replicas roll. Changing a value in the auth secret does not redeploy anything by itself; run cpln workload force-redeployment RELEASE_NAME-langfuse-web --gvc GVC_NAME and the same for RELEASE_NAME-langfuse-worker.

Troubleshooting

Symptom: cpln helm install succeeded, but RELEASE_NAME-langfuse-web and RELEASE_NAME-langfuse-worker stay not-ready with no container output. status.versions[].message reads The secret <name> no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.Cause: the secret named by langfuse.auth.secretName (or, on GCS, objectStore.gcp.credentialsSecretName) did not exist at install time.Fix: create it as shown in Prerequisites. The deployment recovers on its own after a few minutes, or force it:
Symptom: a second Langfuse release in the same org fails at install with The resource 'my-langfuse-db-credentials' cannot be updated because it is being managed by a different release and creates nothing.Cause: postgres.config.credentialsSecretName names an org-wide secret, and the first release owns the default name. Nothing is shared, overwritten or deleted; the first release is unaffected.Fix: give the second release its own --set postgres.config.credentialsSecretName=... and install again.
Symptom: helm upgrade aborts with langfuse.auth.nextAuthSecret was removed in 1.1.0 ... (or the same for encryptionKey, salt, objectStore.gcp.accessKeyId, objectStore.gcp.secretAccessKey, langfuse.firewall.inboundAllowCIDR).Cause: your values file still carries a 1.0.x key.Fix: follow Upgrading from 1.0.0 or 1.0.1.
Symptom: langfuse.publicUrl must include the scheme, e.g. https://langfuse.example.com.Cause: langfuse.publicUrl was set to a bare hostname.Fix: prefix it with https:// (or http://).
Symptom: the sign-up form reports registration as disabled, and POST /api/auth/signup returns HTTP 422 with {"message":"Sign up is disabled."}.Cause: langfuse.auth.disableSignup is true, the default. This is the intended closed instance.Fix: sign in with the adminEmail / adminPassword from your auth secret and invite users from inside Langfuse, or set langfuse.auth.disableSignup: false and upgrade if you want self-service registration.
Symptom: you updated adminPassword (or adminEmail) in the auth secret and redeployed, but the login did not change.Cause: headless initialization only creates the owner account when it does not exist yet; it does not update an existing user.Fix: change the password from inside Langfuse under the user’s settings. The values in the secret matter only for a fresh database.
Symptom: the first log lines of RELEASE_NAME-langfuse-web and RELEASE_NAME-langfuse-worker show Can't reach database server or Applying database migrations failed while RELEASE_NAME-postgres is still starting.Cause: startup ordering. The web and worker tiers retry and apply migrations once PostgreSQL accepts connections.Fix: none needed if the workloads reach ready. If they keep failing, check RELEASE_NAME-postgres with cpln workload get-deployments RELEASE_NAME-postgres --gvc GVC_NAME -o yaml and its logs:
Symptom: RELEASE_NAME-langfuse-clickhouse becomes ready, but writes to the bucket fail with an access or signature error in its logs.Cause: on the gcp path ClickHouse reads the HMAC pair from the accessKeyId and secretAccessKey keys of the secret named by objectStore.gcp.credentialsSecretName. A wrong key name, a key for a different service account, or a service account without roles/storage.objectAdmin all surface here.Fix: check the secret’s keys with cpln secret reveal GCS_CREDENTIALS_SECRET_NAME -o yaml, confirm the HMAC key belongs to the service account that was granted access, then force a redeployment:

Important Notes

  • Create the auth secret before installing; a missing one leaves the web and worker workloads waiting on a secret that does not exist.
  • encryptionKey can never be rotated: it encrypts every provider key stored under Settings → LLM Connections, and changing it makes all of them unreadable. Keep a copy outside Control Plane and never delete the secret.
  • Sign-up is closed by default and the owner account comes from your secret. Record adminPassword — it is displayed nowhere, and with sign-up closed there is no other way to create the first account.
  • Change postgres.credentials.password, redis.auth.password and clickhouse.config.password before installing; they ship as change-me- placeholders and are used exactly as written.
  • Give each install its own bucket. Two installs on one bucket share ClickHouse data parts under clickhouse/.
  • Give each release its own postgres.config.credentialsSecretName; secret names are org-wide and a second release on the default name is refused at install.
  • Serving Langfuse on your own domain requires langfuse.publicUrl, including the scheme.
  • With publicAccess.enabled: false there is no browser path to the UI; only other workloads in the GVC can reach the API.
  • Prerequisite secrets are not owned by the release: the auth and GCS credentials secrets survive helm uninstall and must be deleted by hand if no longer wanted.
  • Uninstall deletes the volume sets, including PostgreSQL’s. Trace data already in your bucket is untouched, but users, projects and API keys are not — back up PostgreSQL first.

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