Skip to content

Deploying on Nebari

Terminal window
kubectl create namespace langfuse
kubectl label namespace langfuse nebari.dev/managed=true
kubectl -n langfuse create secret generic langfuse-secrets \
--from-literal=salt=$(openssl rand -hex 16) \
--from-literal=encryptionKey=$(openssl rand -hex 32) \
--from-literal=nextauth-secret=$(openssl rand -hex 32) \
--from-literal=postgres-password=$(openssl rand -hex 16) \
--from-literal=redis-password=$(openssl rand -hex 16) \
--from-literal=clickhouse-password=$(openssl rand -hex 16) \
--from-literal=root-user=langfuse \
--from-literal=root-password=$(openssl rand -hex 16)

Hex values only — these end up in datastore connection strings, where /, +, and @ need URL-encoding and produce parse errors that look like authentication failures.

From examples/argocd-app.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: langfuse
namespace: argocd
spec:
project: default
source:
repoURL: https://nebari-dev.github.io/helm-repository
chart: nebari-langfuse
targetRevision: 0.1.0
helm:
releaseName: langfuse
valuesObject:
nebariapp:
enabled: true
hostname: langfuse.example.com
auth:
enabled: true
landingPage:
enabled: true
secrets:
generate: false
langfuse:
langfuse:
nextauth:
url: https://langfuse.example.com # must equal nebariapp.hostname
auth:
providers:
keycloak:
issuer: "https://keycloak.example.com/realms/nebari"
destination:
server: https://kubernetes.default.svc
namespace: langfuse
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
managedNamespaceMetadata:
labels:
nebari.dev/managed: "true"

Three values to change: the hostname (twice — nebariapp.hostname and nextauth.url must match) and the Keycloak issuer.

Note langfuse.langfuse in that values block. It is not a typo — see Value nesting.

releaseName: langfuse keeps the web Service named langfuse-web, which is what nebariapp.service.name targets. Combined with the chart’s langfuse.fullnameOverride: langfuse pin, the name is stable.

managedNamespaceMetadata applies nebari.dev/managed: "true". Without it the operator refuses to reconcile the NebariApp: it emits a Warning event and sets Ready=False with reason NamespaceNotOptedIn. You already applied the label by hand above; this keeps it if the namespace is ever recreated.

valuesObject rather than a values string means Argo CD parses the YAML, so a malformed block fails the sync instead of reaching Helm as an opaque string. It does not protect you from the wrong depth — a misplaced value is still valid YAML and still silently ignored.

targetRevision: 0.1.0 should stay pinned. A floating version would upgrade Langfuse and run its schema migrations unannounced.

selfHeal: true reverts manual edits. Change values in git.

The chart defaults nebariapp.routing.enabled: true. Do not remove the block.

Without spec.routing, the operator skips routing entirely — the NebariApp reports RoutingNotConfigured, no HTTPRoute or certificate is created, and the hostname returns 404 while everything else looks healthy.

Terminal window
kubectl -n argocd get application langfuse
kubectl -n langfuse get nebariapp,httproute,certificate
kubectl get namespace langfuse -o jsonpath='{.metadata.labels}'
# The Secret must be stable across syncs
kubectl -n langfuse get secret langfuse-secrets -o jsonpath='{.data.salt}' | sha256sum

Run that last command before and after a sync. A changed digest means secrets.generate is still true and the credentials are rotating.

Then the app itself:

Terminal window
kubectl -n langfuse get pods
kubectl -n langfuse logs deploy/langfuse-web --tail=50

First sync takes several minutes — four datastores start, then Langfuse runs schema migrations against PostgreSQL and ClickHouse before langfuse-web becomes ready.

The pre-created langfuse-secrets is not managed by the chart, so Argo CD can report it as out of sync. Add an ignoreDifferences entry for the Secret resource if the noise matters.

Bump targetRevision and commit.

After any values refactor, confirm secrets.generate is still false. Reintroducing true rotates the credentials on the next sync.