Skip to content
GitHub

Production install

Item Requirement Notes
Kubernetes ≥ 1.29 k3s, kind, EKS, GKE or AKS
StorageClass with ReadWriteOnce required for the chart’s own PostgreSQL users, sessions, releases and the audit log live on its 8 GiB volume; apps with volumes also use it
Gateway API CRDs and a Gateway controller for public app access Traefik v3, Envoy Gateway or Cilium
cert-manager for HTTPS run with Gateway API support: --set config.enableGatewayAPI=true
metrics-server for autoscaling the HPA does not work without it; k3s ships it
A CNI that enforces NetworkPolicy for tenant isolation Calico and Cilium do, and k3s does with its built-in policy controller; plain flannel elsewhere does not

kuben doctor checks all of the above from inside the pod or from a workstation with a kubeconfig.

Terminal window
kuben doctor --cluster

It reads your kubeconfig, changes nothing, and lists what each feature needs and what is missing (Gateway, HTTPS, volumes, isolation, autoscaling), with the permissions Kuben needs. Without a Gateway controller, Envoy Gateway is the documented choice; set platform.gatewayClassName=eg then. Kuben uses what the cluster has and never upgrades CRDs it does not own. helm install --dry-run=server validates the chart against the cluster before anything is created.

Terminal window
helm install kuben oci://ghcr.io/teamtem-dev/charts/kuben \
--namespace kuben-system --create-namespace \
--set publicUrl=https://kuben.example.com \
--set platform.baseDomain=apps.example.com \
--set platform.gatewayClassName=traefik \
--set platform.clusterIssuer=letsencrypt

The values that matter most:

Value Default Purpose
publicUrl empty the URL users open; used for absolute links
admin.email admin@kuben.local initial admin
admin.existingSecret empty Secret with a password key. Empty: a password is generated on first start into the Secret kuben-initial-admin, never logged
database.url / database.existingSecret empty: the chart’s own PostgreSQL an external PostgreSQL; the chart then runs none of its own. Use one for several replicas or high availability
postgresql.persistence.size 8Gi the volume of the chart’s own PostgreSQL. helm uninstall keeps it and the Secret with its passwords
security.cookieSecure true __Host- cookie with Secure. auto follows publicUrl (https → Secure); false only for local development over HTTP
platform.* empty the KubenConfig singleton: baseDomain, gateway, clusterIssuer
route.enabled false publish Kuben’s own console through an HTTPRoute on your Gateway

The full list is in the Helm chart reference. The Gateway and the ClusterIssuer themselves are described in Exposing apps and HTTPS.

The Kuben pod runs:

  • as a non-root user (UID 65532), from a distroless image
  • with a read-only root filesystem, all capabilities dropped and the RuntimeDefault seccomp profile
  • with rolling updates (maxUnavailable: 0, maxSurge: 1), so an upgrade needs no downtime
  • with KUBEN_SECURITY__TRUST_FORWARDED_FOR=true, because it sits behind the Gateway, which appends the client address
  • with KUBEN_KUBE__LEADER_ELECTION=true, so several replicas never run the controllers twice

Requests default to 50m CPU and 64 Mi of memory, with a 256 Mi limit. A pod reports ready only after its informers have listed every Project, Environment, App and Pod once, so a fresh replica never answers 404 for objects that exist.

Unless database.url or database.existingSecret is set, the chart also runs PostgreSQL 17 as a single-instance StatefulSet kuben-postgresql (requests 50m CPU and 128 Mi, limit 512 Mi). Kuben connects to it as the ordinary role kuben, which owns the database kuben, so row-level security applies; the superuser postgres is for maintenance only. kuben doctor warns (database role) when Kuben connects as a superuser or a BYPASSRLS role. Details are in the Helm chart reference.

Either put your own ingress in front of the kuben Service (port 80), or let the chart create an HTTPRoute on the same Gateway the apps use:

Terminal window
helm upgrade kuben oci://ghcr.io/teamtem-dev/charts/kuben -n kuben-system --reuse-values \
--set route.enabled=true \
--set route.hostname=kuben.example.com \
--set route.gateway.name=kuben --set route.gateway.namespace=kuben-system

Whichever you choose, the console must be served over HTTPS: the session cookie is Secure.

  1. Read the generated password:

    Terminal window
    kubectl -n kuben-system get secret kuben-initial-admin -o jsonpath='{.data.password}' | base64 -d; echo
  2. Open the console (publicUrl, or a port-forward while the route is not ready), sign in as admin.email and change the password under Account.

  3. Delete the bootstrap Secret:

    Terminal window
    kubectl -n kuben-system delete secret kuben-initial-admin
  4. Invite your team from Team; see Teams and roles.

If the admin password is ever lost:

Terminal window
kubectl -n kuben-system exec deploy/kuben -- /kuben reset-admin