Skip to content
GitHub

Helm chart values

The chart is published as an OCI artifact:

Terminal window
helm show values oci://ghcr.io/teamtem-dev/charts/kuben
Value Default
image.repository ghcr.io/teamtem-dev/kuben multi-arch (amd64, arm64), distroless, non-root
image.tag the chart’s appVersion
image.pullPolicy IfNotPresent
imagePullSecrets []
Value Default
replicaCount 1 every replica serves the API; only the holder of the kuben-controller Lease runs the controllers. For several replicas or high availability use an external PostgreSQL: the chart’s own is a single instance
database.url "" an external PostgreSQL, e.g. postgres://kuben:…@db.example.com/kuben
database.existingSecret "" Secret with a url key (PostgreSQL credentials); wins over database.url

With database.url and database.existingSecret both empty, the chart runs its own PostgreSQL; with either set, it renders none. See High availability.

Value Default
postgresql.image.repository docker.io/library/postgres
postgresql.image.tag "17-alpine"
postgresql.image.digest the release’s pin from the bundle lock (kuben version --bundle); empty pulls the tag
postgresql.image.pullPolicy IfNotPresent
postgresql.persistence.size 8Gi
postgresql.persistence.storageClass "" the cluster default; it must offer ReadWriteOnce volumes
postgresql.resources requests cpu: 50m, memory: 128Mi; limits memory: 512Mi

It is a single-instance StatefulSet <fullname>-postgresql with the volume data-<fullname>-postgresql-0; for a release named kuben the fullname is kuben. Kuben connects as the ordinary role kuben, which owns the database kuben, so row-level security applies to it. The superuser postgres is for maintenance only.

The passwords are in the Secret <fullname>-postgresql: password for the role kuben, postgres-password for postgres. helm uninstall keeps that Secret and the volume, and a re-install finds the data with the same password. To start over, delete both:

Terminal window
kubectl -n kuben-system delete pvc/data-kuben-postgresql-0 secret/kuben-postgresql
Value Default
secrets.existingKeyringSecret "" Secret with a secrets.keyring key (version:base64-key lines). Empty: the chart makes <fullname>-secrets-keyring with one random key and keeps it on helm uninstall

Managed secret values are sealed with the keys of this keyring, which never enters the database. Every replica and the backup job mount the same Secret. Back it up apart from the database: a restore needs it. See Backup and restore.

Value Default
backup.enabled true a CronJob <fullname>-backup that runs kuben backup
backup.schedule "17 3 * * *" cron schedule
backup.keep 7 backups kept on the volume
backup.maxAgeHours 26 Kuben reports itself degraded when the newest good backup is older (0 when backups are off)
backup.includeKeyring false also copy the keyring into each backup; whoever has such a backup can read every secret
backup.beforeUpgrade true on helm upgrade, a hook Job takes a backup and runs the new version’s kuben upgrade-check; a failure stops the upgrade before anything changed
backup.upgradeCheckMajor false pass --major to that kuben upgrade-check: set true for the one upgrade across a major version (1.2 → 2.0), after reading its release notes
backup.image "" an image with PostgreSQL client tools at least as new as the server; empty: the chart’s PostgreSQL image
backup.deadlineSeconds 3600 deadline of a backup Job
backup.persistence.size 20Gi the backup volume <fullname>-backup, kept on helm uninstall
backup.persistence.storageClass "" the cluster default
backup.persistence.existingClaim "" write to this claim instead of making one
backup.resources requests cpu: 50m, memory: 64Mi; limits memory: 512Mi

The backup Job copies the kuben binary out of Kuben’s image and runs it next to PostgreSQL’s client tools, without a service-account token. The pre-upgrade hook runs only when the backup volume exists. Copy backups off the cluster: a backup on the same storage is no backup.

Value Default
legacy.sqliteVolumeAcknowledged false true: upgrade over the SQLite volume of 1.0 or 1.1 and start with a new, empty database

Kuben 1.0 and 1.1 kept their data in SQLite on the volume <fullname>-data, which this chart does not read. The persistence.* values that created it are gone. An upgrade of a release that still has that volume stops with a message, so that a new, empty database is never taken for lost data. With legacy.sqliteVolumeAcknowledged=true it goes ahead; the old volume stays untouched.

Value Default
admin.email admin@kuben.local initial admin
admin.existingSecret "" Secret with a password key. Empty: generated on first start into kuben-initial-admin, never logged
publicUrl "" e.g. https://kuben.example.com
security.cookieSecure true keep true in production; auto follows publicUrl

The chart always sets KUBEN_SECURITY__TRUST_FORWARDED_FOR=true (Kuben sits behind the Gateway), KUBEN_KUBE__REQUIRED=true and KUBEN_KUBE__LEADER_ELECTION=true.

Value Default
platform.create true write the KubenConfig singleton
platform.baseDomain "" apps get <app>-<environment>.<baseDomain>
platform.gatewayClassName "" GatewayClass of the Gateway Kuben creates and owns (kuben-system/kuben unless platform.gateway is set)
platform.gateway "" Gateway API Gateway for app routes, as namespace/name. Without gatewayClassName it must exist and carry the label kuben.dev/gateway-owner=kuben
platform.gatewayPorts {} listener ports of Kuben’s Gateway, {http, https}; default 80 and 443. Traefik needs {http: 8000, https: 8443}
platform.clusterIssuer "" cert-manager ClusterIssuer; enables HTTPS once it is Ready

The chart sets only these fields of KubenConfig. The others (wildcardTlsSecret, registry, sizes) are set on the resource itself, for example kubectl patch kubenconfig kuben --type merge -p '{"spec":{"wildcardTlsSecret":"apps-wildcard"}}'; see Custom resources.

Value Default
sso.enabled false sign-in with an OpenID Connect provider; needs publicUrl, sso.issuer, sso.clientId and sso.existingSecret
sso.issuer "" the provider’s issuer URL
sso.clientId ""
sso.existingSecret "" Secret with the client secret, mounted as a file (KUBEN_SSO__CLIENT_SECRET_FILE)
sso.clientSecretKey client-secret its key in that Secret
sso.displayName Single sign-on the sign-in button
sso.scopes [openid, email, profile]
sso.groupClaim groups the ID token claim with the person’s groups
sso.groups {} provider group → role (viewer, developer, admin, owner), applied at every sign-in
sso.defaultRole "" role of people in no mapped group; empty refuses them
sso.allowedDomains [] email domains allowed; empty allows any
sso.requireVerifiedEmail true
sso.org "" organization people join; empty: the bootstrap organization
sso.disablePasswordForLinked false linked accounts cannot also sign in with a password

Register <publicUrl>/api/v1/auth/sso/callback at the provider. See Sign-in and security.

Value Default
ci.githubActions.enabled false GitHub Actions may exchange its OIDC token at <publicUrl>/api/v1/ci/github/token for a short-lived Kuben token under a trust policy (Settings → CI trust policies)
ci.githubActions.audience "" the audience workflows request; empty: publicUrl

See Deploy from CI.

Value Default
git.github.appId "" the GitHub App’s numeric id; empty turns Git sources off
git.github.existingSecret "" Secret with the App’s private key (file KUBEN_GIT__GITHUB_PRIVATE_KEY_FILE) and webhook secret
git.github.privateKeyKey private-key.pem key of the PEM private key in that Secret
git.github.webhookSecretKey webhook-secret key of the webhook secret in that Secret
git.github.apiUrl / git.github.cloneUrl https://api.github.com / https://github.com for GitHub Enterprise Server
build.enabled false run the build worker; sources come from git.github, from the organizations’ Git connections, or both
build.namespace "" namespace of build Jobs; empty: the release namespace
build.buildkitImage, build.fetchImage, build.scannerImage "" empty: the server’s defaults; pin by digest in production
build.railpackFrontend, build.railpackImage "" both set: Railpack builds for sources without a Dockerfile
build.maxConcurrent / build.maxConcurrentPerOrg 2 / 1 builds at once
build.pushSecret "" a kubernetes.io/dockerconfigjson Secret in the build namespace with push access
build.insecureRegistry false push over plain HTTP
build.nodePool "" node selector key=value of a build pool
build.cpuRequest, build.cpuLimit, build.memory, build.ephemeralStorage, build.deadlineSeconds empty / 0 per build; empty keeps 500m, 2, 2Gi, 10Gi and 30 minutes
build.logTailKiB 0 how much of the end of each build’s log is kept after the build, 1 to 1024 KiB; 0 keeps the server’s 256
integrations.allowPrivateHosts false let Git connections and organization registries reach private, loopback and link-local addresses (a GitLab, Gitea or registry inside your network); every organization admin can then reach them through Kuben

GitHub delivers the App’s webhooks to <publicUrl>/api/v1/webhooks/github.

Token connections to GitHub, GitLab and Gitea/Forgejo, and organization registry logins, are not chart values: organization admins add them at runtime under Settings → Integrations and Settings → Registries, and their secrets are sealed with the keyring (see Git providers and Container registries). Their push webhooks go to:

Provider Webhook URL Verified with
GitHub App <publicUrl>/api/v1/webhooks/github the App’s webhook secret
GitLab <publicUrl>/api/v1/webhooks/gitlab/<connection id> X-Gitlab-Token
Gitea / Forgejo <publicUrl>/api/v1/webhooks/gitea/<connection id> X-Gitea-Signature (HMAC-SHA256)

Set publicUrl and make it reachable from the Git host.

Every value is an environment variable of the Kuben pod (KUBEN_SSO__*, KUBEN_CI__*, KUBEN_GIT__*, KUBEN_BUILD__*, KUBEN_INTEGRATIONS__*, see Configuration); secrets come from the Secrets you name and are never written into the pod spec. A value that needs another (for example sso.enabled without sso.issuer) stops helm install with a message.

Value Default
agent.enabled true run the cluster agent (ADR-027): a separate, unprivileged pod that carries apps out. It enrolls on its own from the Secret kuben-agent-enrollment Kuben keeps (a short-lived token until it has a certificate) and keeps its identity in kuben-agent-identity. Apps made while it is linked are delivered by it; earlier ones stay with the App controller until handed over
agent.port 7443 the port Kuben listens on for agents (service port agentlink)

The agent requests 10m CPU and 24 MiB of memory (limit 96 MiB). Kuben keeps the CA agents pin in the Secret kuben-agentlink-ca. Run a single Kuben replica with the agent until high availability lands.

Value Default
route.enabled false expose the console through an HTTPRoute
route.hostname ""
route.gateway.name / route.gateway.namespace ""
Value Default
service.type ClusterIP
service.port 80
metrics.port 9090
logLevel info
resources.requests cpu: 50m, memory: 64Mi
resources.limits memory: 256Mi no CPU limit
podAnnotations, nodeSelector, tolerations, affinity {} / []
  • A Deployment with RollingUpdate (maxUnavailable: 0, maxSurge: 1), and a PodDisruptionBudget for several replicas.
  • Without an external database: the PostgreSQL StatefulSet, its Service and its Secret, and an init container in the Kuben pod that waits for PostgreSQL to answer.
  • A security context: runAsNonRoot, UID 65532 (numeric, so Kubernetes can verify it), read-only root filesystem, all capabilities dropped, RuntimeDefault seccomp.
  • Liveness and readiness probes on /livez and /readyz.
  • A ServiceAccount, a ClusterRole for the tenant resources it manages and a namespaced Role for the controller Lease. The full list is in templates/rbac.yaml.
  • The CRDs in crds/. Helm installs them once; the binary server-side-applies them again on every start so upgrades pick up new fields.
  • The KubenConfig singleton from platform.*, kept on helm uninstall.
  • The keyring Secret (unless secrets.existingKeyringSecret), kept on helm uninstall.
  • With backup.enabled: the backup volume (kept on helm uninstall), the CronJob, and on upgrades the pre-upgrade hook Job.
  • With agent.enabled: the cluster agent’s Deployment, its ServiceAccount and RBAC (files/agent.yaml, the same manifest kuben setup applies).

The chart is linted with helm lint --strict, rendered and scanned by Trivy for misconfigurations on every pull request; accepted findings are justified in .trivyignore.yaml.