Prerequisites
- An ArgoCD installation, version 2.6+ (for multiple sources when you keep production values in a separate config repo; the single-source pattern below works on any modern Argo)
- A Kubernetes cluster that meets the Helm chart prerequisites
- A namespace for LangWatch (the manifest below creates one with
CreateNamespace=true)
Quick start
The langwatch chart needs these operator-managed Secrets in your target namespace before the first sync:langwatch-app-secretsholds the app keys + the AI Gateway shared-auth keys. Bothlangwatch-appand the gateway pod mount from this Secret.- A ClickHouse credentials Secret (any name, referenced from values via
clickhouse.auth.existingSecret) holds the ClickHouse password and the Keeper inter-node cluster secret. The deployment composesCLICKHOUSE_URLat runtime from the password key, so the Secret name must be a non-default name (the chart’s render-time validator fails fast if you leave it at the default<release>-clickhousewhileautogen.enabled=false). - A PostgreSQL Secret (
postgresql.auth.existingSecret, keypostgres-password) and a Redis Secret (redis.auth.existingSecret, keypassword), when the chart runs those databases. Withautogen.enabled=falsethe render fails without them. Skip them if you setpostgresql.chartManaged: falseandredis.chartManaged: false.
langwatch namespace from your secret manager. The keys each one needs depend on which components you run (gateway, Langy, LangWatchQL, replicated ClickHouse); see Provide the Secrets for the list.
Then apply the Application manifest:
secrets.existingSecret or autogen.secretNames.app to a name other than langwatch-app-secrets, also set gateway.secrets.existingSecretName and langyagent.secrets.existingSecretName to that name (the chart fails fast at render time if they do not match).
Apply:
CreateContainerConfigError, naming the key. kubectl -n langwatch describe pod shows which.
Production overrides
Thevalues: block in the manifest above accepts the same overrides as a direct helm install. For sizing, ingress, external databases, replicated ClickHouse, and the rest of the production checklist, follow the Kubernetes (Helm) production guide and either paste the values into the helm.values field, or use ArgoCD’s multiple sources pattern (ArgoCD 2.6+) to keep the chart reference while pulling values from your config repo:
ref: values source is value-only (no manifests rendered) and the $values/... path in valueFiles resolves into that source’s checkout. Inlining the values directly in helm.values from the quick-start manifest is also fine if you prefer to keep everything in one place.
Topic-specific configuration pages:
- Sizing and scaling for replica counts, CPU/memory, and the size overlays the chart ships
- Environment variables for the full list of app/worker/gateway env vars
- SSO for OIDC and SAML connections
- Editions and licensing for
app.license.key, which takes an activation code (needs outbound access toconnect.langwatch.ai) or a signed license key (works offline) - Backups for ClickHouse cold storage and PostgreSQL backups
- AI Gateway → Self-hosting → Helm for per-environment gateway tuning if you enable the gateway sub-chart
Operator-managed Secrets via External Secrets / Sealed Secrets / Vault
You can use anExternalSecret, SealedSecret, vaultauth resource, or any other operator that materialises a Kubernetes Secret with the same name and keys. The chart only requires the live Secret to exist at the name you point it at (secrets.existingSecret).
The Secret can land in the same sync as the chart, with one ordering rule.
With chart-managed ClickHouse, the LangWatchQL render Job runs at sync wave -1 and reads the app, PostgreSQL and ClickHouse Secrets. Argo waits for that Job before it applies wave 0, and the Job fails after 5 minutes if a Secret is missing. Put your ExternalSecret (or equivalent) at an earlier wave, for example argocd.argoproj.io/sync-wave: "-3". Other pods wait in CreateContainerConfigError until the operator writes the Secret, then start on their own.
Azure Key Vault: secret names allow only letters, digits and Prefer External Secrets Operator over the Secrets Store CSI driver: the CSI driver only syncs a Kubernetes Secret while a pod mounts its volume.
-, so keys like LW_GATEWAY_INTERNAL_SECRET cannot be stored under that name. Map them in the ExternalSecret:Reinstall and ownership
When you use the operator-managed pattern from the quick-start (autogen.enabled=false, the Secret created by you), the Secret lifecycle is entirely yours. argocd app delete leaves the Secret alone (the chart never owned it), and a fresh argocd app create re-syncs cleanly without any ownership handoff.
Smoke test
After the first sync completes, verify the pods are running on the credentials you provided:argocd app diff should show no diff against the live cluster. To confirm subsequent reconciles do not roll the gateway pods, capture the Secret’s resourceVersion and trigger a sync:
resourceVersion values should be identical because the chart no longer renders the Secret on the operator-managed path.
Then follow First run to create the first account, add the license and connect your identity provider.
Legacy: autogen.enabled=true under ArgoCD
If you setautogen.enabled=true in your Argo Application values, the chart materialises the Secret itself using helm’s lookup-or-randAlphaNum pattern. Under direct helm upgrade that pattern is idempotent: the lookup reads existing values from the live cluster and the manifest reuses them verbatim.
Under ArgoCD it is not idempotent. The argocd-repo-server has no cluster access, by design, because it is a sandboxed renderer, so lookup returns nil on every sync and the chart emits a fresh random pair every render. Without mitigation, every Argo sync rotates the credentials and rolls both the app and the gateway pods.
The mitigation is the same trio that earlier chart versions required from the start: ignoreDifferences on the Secret’s /data (Argo excludes it from the diff), RespectIgnoreDifferences=true in syncOptions (the sync operation itself omits the ignored field), and ServerSideApply=true (server-side apply honors RespectIgnoreDifferences reliably; client-side apply does not). Drop any one of the three and an ordinary sync rotates the live Secret. argocd app sync --force and replace: true bypass RespectIgnoreDifferences and rotate regardless.
<release>-clickhouse, <release>-postgresql and <release>-redis to ignoreDifferences the same way. The LangWatchQL password Secret (<release>-lwql-passwords) is a Helm hook, which Argo recreates on every sync with new values and ignoreDifferences does not cover. Set lwql.enabled: false on this path, or move to operator-managed Secrets.
The recommended path is to flip autogen.enabled to false, provide the Secrets per the quick-start above, and remove the ignoreDifferences block entirely. The legacy path is preserved here for installs that need zero pre-install steps and are willing to apply the trio.
Reference
- Chart template comment:
charts/langwatch/templates/app/secrets.yamldocuments the per-key lookup-or-rand shape inline. - Chart values:
charts/langwatch/values.yamlsectionssecrets,gateway.secrets,autogen. - Helper that enforces app+gateway Secret-name consistency:
charts/langwatch/templates/_helpers.tpldefineslangwatch.appSecretNameandlangwatch.validateSecrets. - Postmortem driving the original chart change: PR #4423.
- Single-secret collapse (this page’s current shape): PR #4440.