Multi-Tenancy
The operator provides two additional CRDs for managing Langfuse organizations and projects declaratively.
Requires a Langfuse Enterprise/Pro self-hosted license
LangfuseOrganization and LangfuseProject are powered by Langfuse's organization-management API, which is an Enterprise/Pro self-hosted feature. On the OSS langfuse/langfuse image this API returns 403 "This feature is not available on your current plan.", so these CRDs do not work without a license.
To use them you must:
- Hold a Langfuse self-hosted Pro or Enterprise license key (
langfuse_ee_...). - Provide it via
spec.eeLicenseKeyon theLangfuseInstance(see below). The operator injects it asLANGFUSE_EE_LICENSE_KEYand the Langfuse server then enables the admin API.
If the license is missing, the operator does not fail the instance — it surfaces a RequiresEELicense condition on the affected LangfuseOrganization/LangfuseProject and leaves a single LangfuseInstance fully functional.
Enabling: EE license key
apiVersion: langfuse.palena.ai/v1alpha1
kind: LangfuseInstance
metadata:
name: production
namespace: langfuse
spec:
eeLicenseKey:
secretRef:
name: langfuse-ee-license
key: license-key
# ... rest of the instance specCreate the Secret first:
kubectl create secret generic langfuse-ee-license \
-n langfuse --from-literal=license-key="langfuse_ee_..."After the instance pods restart with LANGFUSE_EE_LICENSE_KEY set, the Organization/Project CRDs reconcile normally.
Prerequisite: ADMIN_API_KEY
The LangfuseOrganization and LangfuseProject controllers manage resources through the Langfuse organization-management API, which authenticates with the instance's ADMIN_API_KEY as a Bearer token.
The operator handles this for you:
Auto-generated (default): if
spec.auth.adminApiKeyis omitted, the operator generates anADMIN_API_KEY, stores it in the instance's auto-generated secret (<instance>-generated-secrets, keyadmin-api-key), injects it into the Langfuse Web/Worker containers, and uses it for org/project reconciliation. No action required.User-provided: to supply your own key, set
spec.auth.adminApiKey.secretRef:yamlapiVersion: langfuse.palena.ai/v1alpha1 kind: LangfuseInstance metadata: name: production namespace: langfuse spec: auth: adminApiKey: secretRef: name: langfuse-admin key: admin-api-keyThe same key is injected into the Langfuse containers and used by the operator, so the two always match.
WARNING
LangfuseOrganization / LangfuseProject CRs reconcile only after the referenced LangfuseInstance is running with ADMIN_API_KEY configured. Creating them against an instance that predates this setting requires restarting the instance pods so the new env var takes effect.
Organizations
A LangfuseOrganization maps to an organization in Langfuse:
apiVersion: langfuse.palena.ai/v1alpha1
kind: LangfuseOrganization
metadata:
name: ml-platform
namespace: langfuse
spec:
instanceRef:
name: production
displayName: "ML Platform Team"
members:
managedExclusively: false
users:
- email: "alice@example.com"
role: owner
- email: "bob@example.com"
role: admin
- email: "carol@example.com"
role: memberMember Management
managedExclusively: false(default) — the operator adds users from the list but never removes users added outside the CRmanagedExclusively: true— the operator enforces the list exactly, removing users not present
Available roles: owner, admin, member, viewer.
Deletion
The operator uses a finalizer (langfuse.palena.ai/organization-cleanup). On deletion:
- Checks for
LangfuseProjectCRs referencing this organization - If projects exist, blocks deletion and sets a
DeletionBlockedcondition - If no projects reference it, deletes the organization via the Langfuse Admin API
- Removes the finalizer
Projects
A LangfuseProject maps to a project within an organization:
apiVersion: langfuse.palena.ai/v1alpha1
kind: LangfuseProject
metadata:
name: ml-team-prod
namespace: langfuse
spec:
instanceRef:
name: production
organizationRef:
name: ml-platform
projectName: "prod-inference"
apiKeys:
- name: default
secretName: langfuse-ml-team-keys
- name: ci-pipeline
secretName: langfuse-ci-keysAPI Key Management
For each entry in spec.apiKeys, the operator:
- Creates an API key in Langfuse via the Admin API
- Stores the key pair in a Kubernetes Secret
The created Secret has this format:
apiVersion: v1
kind: Secret
metadata:
name: langfuse-ml-team-keys
type: Opaque
data:
publicKey: <base64> # pk-lf-...
secretKey: <base64> # sk-lf-...
host: <base64> # https://langfuse.example.comWorkloads can mount these directly:
env:
- name: LANGFUSE_PUBLIC_KEY
valueFrom:
secretKeyRef:
name: langfuse-ml-team-keys
key: publicKey
- name: LANGFUSE_SECRET_KEY
valueFrom:
secretKeyRef:
name: langfuse-ml-team-keys
key: secretKey
- name: LANGFUSE_HOST
valueFrom:
secretKeyRef:
name: langfuse-ml-team-keys
key: hostWiring clients to a LangfuseProject
The Secret created by the operator (spec.apiKeys[].secretName) contains three keys that match the env-var convention used by virtually every Langfuse client SDK:
| Secret key | Typical client env var |
|---|---|
publicKey | LANGFUSE_PUBLIC_KEY |
secretKey | LANGFUSE_SECRET_KEY |
host | LANGFUSE_BASE_URL (or LANGFUSE_HOST) |
So any pod that wants to send traces to Langfuse just mounts those keys as env vars — no glue code required.
Example: LibreChat
LibreChat (reads LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY / LANGFUSE_BASE_URL):
apiVersion: langfuse.palena.ai/v1alpha1
kind: LangfuseProject
metadata:
name: librechat
namespace: librechat
spec:
instanceRef:
name: production
namespace: langfuse
organizationRef:
name: ml-platform
projectName: "LibreChat"
apiKeys:
- name: librechat
secretName: librechat-langfuse-keysThen in your LibreChat Deployment:
containers:
- name: librechat
env:
- name: LANGFUSE_PUBLIC_KEY
valueFrom: { secretKeyRef: { name: librechat-langfuse-keys, key: publicKey } }
- name: LANGFUSE_SECRET_KEY
valueFrom: { secretKeyRef: { name: librechat-langfuse-keys, key: secretKey } }
- name: LANGFUSE_BASE_URL
valueFrom: { secretKeyRef: { name: librechat-langfuse-keys, key: host } }The same pattern wires any other client (Helicone, Promptfoo, custom Node/Python apps using langfuse / langfuse-python SDKs) — only the destination env-var names differ.
Cross-cluster clients
The host value the operator writes is the in-cluster service URL (http://<instance>-web.<ns>.svc:3000). If the client lives in a different cluster or outside the cluster entirely, override LANGFUSE_BASE_URL to your external Ingress/Route URL instead of using the Secret's host key.
Project Deletion
The finalizer langfuse.palena.ai/project-cleanup:
- Revokes all API keys via the Admin API
- Deletes the associated Kubernetes Secrets
- Optionally deletes the project in Langfuse (controlled by annotation
langfuse.palena.ai/delete-on-remove: "true", default: keep)