Datastores
Langfuse needs four stateful backends. All are bundled by default; all should be external in production.
| Backend | Holds | Losing it means |
|---|---|---|
| PostgreSQL | projects, users, prompts, API keys, config | everything except trace data |
| ClickHouse | traces, observations, scores | all observability history |
| Redis | ingestion queue, cache | in-flight events; the rest recovers |
| S3 / MinIO | large payloads — prompts and completions above the inline limit | trace bodies, while metadata survives |
Two databases rather than one because the workloads are different: PostgreSQL for transactional metadata, ClickHouse for high-volume append-only analytics over traces.
ClickHouse is single-node here, deliberately
Section titled “ClickHouse is single-node here, deliberately”langfuse: clickhouse: clusterEnabled: false replicaCount: 1 zookeeper: enabled: falseThe upstream chart defaults to a 3-replica cluster with ZooKeeper. This pack overrides that.
Langfuse runs its schema migrations over the load-balanced ClickHouse Service, and
golang-migrate’s schema_migrations bookkeeping is not consistently replicated across the
three nodes. Migrations land on different replicas, get marked dirty, and langfuse-web
crash-loops.
Single-node avoids that entire class of failure and is far lighter: upstream’s default is three ClickHouse replicas plus a three-node ZooKeeper ensemble, so this is one pod instead of six.
The bundled images
Section titled “The bundled images”All four use bitnamilegacy/* images pinned by upstream chart 1.5.34.
They also pull from Docker Hub, so restricted egress or rate limiting shows up as image-pull failures:
kubectl -n langfuse get events --field-selector reason=FailedIn a restricted environment, mirror them internally and override the repository, or go external.
Going external
Section titled “Going external”examples/prod-external-datastores.yaml has a full example. The shape:
langfuse: postgresql: deploy: false host: postgres.example.com port: 5432 auth: username: langfuse database: langfuse existingSecret: langfuse-db-credentials redis: deploy: false host: redis.example.com clickhouse: deploy: false host: clickhouse.example.com s3: deploy: false bucket: my-langfuse-bucketdeploy: false removes the bundled subchart and switches the connection settings to the
host you supply. Each can be moved independently — a common intermediate step is external
PostgreSQL and ClickHouse with bundled Redis and MinIO.
Exact key names belong to the upstream chart; the configuration reference has the full set.
Sizing
Section titled “Sizing”The bundled defaults are development-scale. What actually drives capacity:
- ClickHouse grows with trace volume, and trace volume grows with LLM call volume. This is the one to plan for; it dominates storage on any active deployment.
- PostgreSQL stays small — projects, users, prompts, and keys are rows.
- Redis holds the ingestion queue. It grows when the worker falls behind and drains when it catches up; sustained growth means the worker is under-provisioned.
- S3 grows with the fraction of payloads exceeding the inline limit, so it scales with prompt and completion size rather than call count.
On a small cluster — fewer than three schedulable nodes — ClickHouse pods commonly sit
Pending on CPU or memory:
kubectl -n langfuse get pods -l app.kubernetes.io/name=clickhousekubectl -n langfuse describe pod <clickhouse-pod>Backups
Section titled “Backups”Nothing here backs anything up.
| Backend | Backup |
|---|---|
| PostgreSQL | pg_dump, or your managed service’s snapshots |
| ClickHouse | BACKUP TABLE, or managed snapshots |
| Redis | none needed — it is a queue |
| S3 / MinIO | bucket replication or lifecycle policy |
On a Nebari cluster with longhorn-backup-pack, the bundled datastores’ PVCs are covered by the cluster-wide schedule if they sit on the default StorageClass. Confirm rather than assume:
kubectl -n longhorn-system get volumes.longhorn.io \ -l recurring-job-group.longhorn.io/default=enabledVolume snapshots of a running database are crash-consistent, not application-consistent. Usually fine; a logical dump before a migration is the stronger guarantee.
Credentials
Section titled “Credentials”All four bundled datastores read from langfuse-secrets, and so does Langfuse — one source
for both ends of every connection. That is why rotating a password there means restarting
the datastore and both Langfuse workloads. See Secrets and GitOps.
External datastores use their own secrets, referenced per-datastore with existingSecret.