Skip to content
GitHub

Configuration

Lowest to highest:

  1. Built-in defaults
  2. /etc/kuben/config.toml
  3. ./kuben.toml (the nearest one: the working directory, else its parents)
  4. --config <file> or KUBEN_CONFIG (if given)
  5. KUBEN_* environment variables. Nested keys are separated by __: KUBEN_SECURITY__SESSION_TTL_HOURS. A value may be written as in TOML: a list [a, b], a table {admins = "admin"}, true, a number; anything else is a string.

Keys Kuben does not read are ignored; a value of the wrong type stops the server at start.

A TOML file uses the same sections:

kuben.toml
[server]
bind = "127.0.0.1:8080"
public_url = "https://kuben.example.com"
[database]
url = "postgres:///kuben?host=/run/postgresql&user=kuben"
[security]
trust_forwarded_for = true
Variable Default Purpose
KUBEN_SERVER__BIND 0.0.0.0:8080 API and console
KUBEN_SERVER__METRICS_BIND 0.0.0.0:9090 Prometheus /metrics
KUBEN_SERVER__ACTIVATOR_BIND 0.0.0.0:8081 reserved for the scale-to-zero activator
KUBEN_SERVER__PUBLIC_URL unset the URL users open; used for absolute links
KUBEN_ROLES all roles of this process: all, api, controller, activator (comma-separated)
KUBEN_SERVER__REQUEST_TIMEOUT_SECS 30 per-request timeout (streams excepted)
KUBEN_SERVER__MAX_BODY_BYTES 1048576 request body limit (1 MiB)
KUBEN_SERVER__STATE_DIR see below files of this installation: the setup token, a generated admin password, the secret keyring and backups by default

KUBEN_ROLES (or kuben serve --roles) overrides server.roles (KUBEN_SERVER__ROLES, a list: ["api", "controller"]).

Without state_dir: /data when it exists (the container image and the chart), else systemd’s $STATE_DIRECTORY, $XDG_STATE_HOME/kuben, ~/.local/state/kuben, or the working directory.

Variable Default Purpose
KUBEN_DATABASE__URL none, required a postgres:// or postgresql:// URL, e.g. postgres://user:pass@host/db
KUBEN_DATABASE__MAX_CONNECTIONS 4 pool size

Kuben keeps its data in PostgreSQL. There is no default URL: without one the server stops with a message that shows how to run a local PostgreSQL:

Terminal window
docker run -d --name kuben-postgres -e POSTGRES_PASSWORD=kuben -p 5432:5432 postgres:17-alpine
export KUBEN_DATABASE__URL=postgres://postgres:kuben@localhost:5432/postgres

SQLite URLs are rejected, and the data of a 1.x install that used SQLite is not carried over.

The container image sets no database; the Helm chart sets KUBEN_DATABASE__URL, to its own PostgreSQL or to database.url. kuben setup writes the URL in the example above: the server’s own PostgreSQL over its Unix socket, with peer authentication and no password. Connect as an ordinary role: kuben doctor warns (database role) when the role is a superuser or has BYPASSRLS, because row-level security is then bypassed.

Variable Default Purpose
KUBEN_KUBE__KUBECONFIG unset path to a kubeconfig; otherwise $KUBECONFIG, then ~/.kube/config, then the pod’s service account
KUBEN_KUBE__CONTEXT unset kubeconfig context to use
KUBEN_KUBE__WATCH_NAMESPACE unset (all) restrict informers to one namespace
KUBEN_KUBE__REQUIRED false (true in the chart) fail to start when no cluster is reachable
KUBEN_KUBE__NAMESPACE the pod’s namespace home of the controller Lease and of kuben-initial-admin
KUBEN_KUBE__LEADER_ELECTION false (true in the chart) run the controllers only on the Lease holder; required when several processes have the controller role
Variable Default Purpose
KUBEN_SECURITY__COOKIE_SECURE auto Secure + __Host- on the session cookie. auto: only when KUBEN_SERVER__PUBLIC_URL is https://, so a plain-http install signs in and turns Secure once a TLS address is set. true behind a TLS proxy without a public URL (the chart’s default); false only for development
KUBEN_SECURITY__SESSION_TTL_HOURS 12 session lifetime
KUBEN_SECURITY__SESSION_CACHE_TTL_SECS 5 upper bound for a revoked session to stay valid on another replica
KUBEN_SECURITY__PASSWORD_MIN_LENGTH 12
KUBEN_SECURITY__ARGON2_M_KIB 19456 Argon2id memory per password hash, KiB (19 MiB)
KUBEN_SECURITY__ARGON2_T 2 Argon2id iterations
KUBEN_SECURITY__ARGON2_P 1 Argon2id parallelism
KUBEN_SECURITY__LOGIN_CONCURRENCY 2 concurrent Argon2id verifications; bounds memory under a login flood
KUBEN_SECURITY__LOGIN_MAX_FAILURES 5 per email + client IP, within the window
KUBEN_SECURITY__LOGIN_MAX_FAILURES_PER_IP 30
KUBEN_SECURITY__LOGIN_MAX_FAILURES_PER_ACCOUNT 100
KUBEN_SECURITY__LOGIN_WINDOW_SECS 900 15 minutes
KUBEN_SECURITY__TRUST_FORWARDED_FOR false (true in the chart) use the last X-Forwarded-For hop as the client address, and X-Forwarded-Proto for whether the request came over HTTPS. Only behind a proxy that sets them
KUBEN_SECURITY__TRUSTED_PROXIES empty addresses or CIDRs (10.42.0.0/16) whose forwarded headers are believed; empty: every peer’s, once trust_forwarded_for is on. kuben setup sets the pod network when the console sits behind Kuben’s Gateway
KUBEN_SECURITY__INSECURE_SETUP false let /setup take the first admin’s password over plain HTTP from another machine. Otherwise only HTTPS (directly or through a trusted proxy) or a loopback client (an SSH tunnel) may; the page says how

See Sign-in and throttling.

Variable Default Purpose
KUBEN_TELEMETRY__LOG_FORMAT json json or pretty
KUBEN_TELEMETRY__LOG_LEVEL info trace, debug, info, warn or error; RUST_LOG wins when set. A 1.x filter such as info,kuben_platform=debug is still accepted, but only its global level counts
KUBEN_TELEMETRY__OTLP_ENDPOINT unset accepted, not used: no traces are exported yet (the support bundle leaves it out)

The 1.x server ran on Tokio, and these keys sized its thread pools. Since 2.0 the server is written in Go: the keys are still accepted, so an existing configuration keeps loading, but they have no effect.

Variable Default Purpose
KUBEN_RUNTIME__WORKER_THREADS unset no effect since 2.0
KUBEN_RUNTIME__MAX_BLOCKING_THREADS 16 no effect since 2.0
KUBEN_RUNTIME__BULKHEAD false no effect since 2.0 (ADR-013: there is no second runtime)

The Go runtime’s own variables take their place. GOMAXPROCS (the threads running Go code at once) defaults to the CPUs available, and follows the container’s CPU limit. GOMEMLIMIT (a soft memory limit, e.g. GOMEMLIMIT=400MiB) is unset by default; set it a little below the container’s memory limit so the garbage collector works harder before the kernel kills the process.

Used on the first start with an empty database. Outside a cluster, when no ADMIN_PASSWORD is set, nothing is seeded: the console shows /setup, where the first admin account is created (see kuben setup-token).

Variable Default Purpose
KUBEN_BOOTSTRAP__ORG_SLUG default
KUBEN_BOOTSTRAP__ORG_NAME Default the name the /setup page asks for wins over this
KUBEN_BOOTSTRAP__ADMIN_EMAIL admin@kuben.local for a configured or generated password; /setup asks for the email
KUBEN_BOOTSTRAP__ADMIN_PASSWORD unset set: the admin is created with it. Unset in Kubernetes: a generated password goes into the Secret kuben-initial-admin. Unset outside: the /setup page. Never logged

These limits apply to every organization of the installation. Only the operator sets them, and unset means unlimited. A deployment that would exceed a limit is refused when Kuben accepts it.

Variable Default Purpose
KUBEN_QUOTA__ORG_CPU unset CPU requests of all apps at their peak, e.g. 32
KUBEN_QUOTA__ORG_MEMORY unset memory requests of all apps at their peak, e.g. 64Gi
KUBEN_QUOTA__ORG_PODS unset pods of all apps at their peak
KUBEN_QUOTA__ORG_APPS unset live apps
KUBEN_QUOTA__ORG_ENVIRONMENTS unset live environments
Variable Default Purpose
KUBEN_BACKUP__DIR backups in the state directory where kuben backup writes
KUBEN_BACKUP__KEEP 7 how many backups are kept
KUBEN_BACKUP__MAX_AGE_HOURS 26 the server reports itself degraded and opens an incident when the newest good backup is older than this; 0 turns the check off
KUBEN_BACKUP__BEFORE_UPGRADE true take a backup before a new version migrates the database
Variable Default Purpose
KUBEN_NOTIFY__ALLOW_PRIVATE_TARGETS false allow webhooks to private, loopback and link-local addresses
KUBEN_NOTIFY__ALLOW_HTTP false allow http:// webhook endpoints

How long rows that only describe the past are kept. See Budgets.

Variable Default Purpose
KUBEN_RETENTION__OUTBOX_DAYS 7 delivered outbox messages
KUBEN_RETENTION__WEBHOOK_DELIVERY_DAYS 30 finished webhook deliveries
KUBEN_RETENTION__RESOLVED_INCIDENT_DAYS 180 resolved incidents
KUBEN_RETENTION__USAGE_DAYS 7 hourly usage of apps
Variable Default Purpose
KUBEN_DOMAINS__REQUIRE_CLAIM false apps may only use custom domains their organization verified
KUBEN_DOMAINS__DOH_URL https://cloudflare-dns.com/dns-query the DNS-over-HTTPS resolver claims are checked with (JSON API)
KUBEN_DOMAINS__CLOUDFLARE_API_URL https://api.cloudflare.com/client/v4 the Cloudflare API
KUBEN_DOMAINS__CNAME_TARGET unset app DNS records become a CNAME to this name instead of A/AAAA records of the Gateway

AgentLink, where cluster agents connect to Kuben (ADR-027).

Variable Default Purpose
KUBEN_AGENT__BIND unset (set in the chart) where Kuben listens for agents (host:port); unset, it does not
KUBEN_AGENT__HUB_NAME hub.kuben.internal the name Kuben’s certificate carries; agents check it
KUBEN_AGENT__CERTIFICATE_HOURS 24 lifetime of the client certificates Kuben issues to agents
KUBEN_AGENT__HEARTBEAT_SECS 10 how often agents send a heartbeat
KUBEN_AGENT__LOCAL false (true in the chart) enroll an agent in Kuben’s own cluster
KUBEN_AGENT__ADVERTISE unset the address agents in the cluster dial (host:port)
KUBEN_AGENT__NAMESPACE Kuben’s own, else kuben-system namespace of the local agent and its Secrets

The GitHub App Kuben acts as. The App is on only when the App id, its key file and the webhook secret are all set. Token connections to GitHub, GitLab and Gitea/Forgejo need no configuration: organization admins add them at runtime, and their tokens are sealed with the keyring (see Git providers).

Variable Default Purpose
KUBEN_GIT__GITHUB_APP_ID unset the App’s numeric id
KUBEN_GIT__GITHUB_PRIVATE_KEY_FILE unset PEM file of the App’s private key (PKCS#1 or PKCS#8 RSA)
KUBEN_GIT__GITHUB_WEBHOOK_SECRET unset the secret GitHub signs webhook deliveries with
KUBEN_GIT__GITHUB_API_URL https://api.github.com the REST API root, for GitHub Enterprise Server
KUBEN_GIT__GITHUB_CLONE_URL https://github.com where build pods clone from

Builds run as one rootless BuildKit Job per attempt (ADR-028). They read sources through the GitHub App, through Git connections, or both; see Builds.

Variable Default Purpose
KUBEN_BUILD__ENABLED false run the build worker
KUBEN_BUILD__NAMESPACE Kuben’s own, else kuben-builds namespace of build Jobs
KUBEN_BUILD__BUILDKIT_IMAGE moby/buildkit:v0.33.0-rootless pin it by digest in production; the worker warns about images that are not
KUBEN_BUILD__FETCH_IMAGE alpine/git:2.49.1 the image with git that fetches the source
KUBEN_BUILD__RAILPACK_FRONTEND unset Railpack’s BuildKit frontend image; with railpack_image, enables Railpack builds
KUBEN_BUILD__RAILPACK_IMAGE unset the image with sh and the railpack CLI; unset, only Dockerfile builds run
KUBEN_BUILD__CPU_REQUEST 500m
KUBEN_BUILD__CPU_LIMIT 2
KUBEN_BUILD__MEMORY 2Gi memory request and limit (equal)
KUBEN_BUILD__EPHEMERAL_STORAGE 10Gi
KUBEN_BUILD__DEADLINE_SECS 1800 hard deadline of one attempt
KUBEN_BUILD__MAX_CONCURRENT 2 builds at once, over all organizations; 0 disables building
KUBEN_BUILD__MAX_CONCURRENT_PER_ORG 1
KUBEN_BUILD__PUSH_SECRET unset a kubernetes.io/dockerconfigjson Secret in the build namespace with push access
KUBEN_BUILD__INSECURE_REGISTRY false push over plain HTTP (an in-cluster registry without TLS)
KUBEN_BUILD__REGISTRY_AUTH_FILE unset user:password the verifier uses
KUBEN_BUILD__NODE_POOL unset node selector key=value of the build pool
KUBEN_BUILD__SCANNER_IMAGE aquasec/trivy:0.74.0 writes the SBOM of every built image and scans it; empty disables scanning
KUBEN_BUILD__SCANNER_MEMORY 1Gi memory request and limit of the scan container
KUBEN_BUILD__RESCAN_HOURS 24 rescan the images apps run when their newest scan is older than this
KUBEN_BUILD__LOG_TAIL_KIB 256 how much of the end of a build’s log is kept when the build ends, so the log outlives the build pod; 1 to 1024, a value outside is the nearest bound

Git connections and organization registries (2.1). Organization admins choose the address of a self-hosted GitLab or Gitea and the server of a registry, and Kuben calls them. By default it refuses any that resolves to a private, loopback, link-local or otherwise non-public address, checked on every connection after the name is resolved. A proxy from HTTPS_PROXY/HTTP_PROXY is used as it is; the target behind it is checked first.

Variable Default Purpose
KUBEN_INTEGRATIONS__ALLOW_PRIVATE_HOSTS false let Git connections and registry checks reach private addresses too, for a Git server or registry inside your network. Every organization admin can then reach those addresses through Kuben

External CI trust: a GitHub Actions workflow exchanges its OIDC token for a short-lived Kuben token under an organization’s trust policy.

Variable Default Purpose
KUBEN_CI__GITHUB_ACTIONS false accept GitHub Actions OIDC tokens
KUBEN_CI__GITHUB_OIDC_ISSUER https://token.actions.githubusercontent.com the only issuer trusted; its keys are read from <issuer>/.well-known/jwks
KUBEN_CI__GITHUB_OIDC_AUDIENCE server.public_url the audience workflows request

Single sign-on with an OpenID Connect provider.

Variable Default Purpose
KUBEN_SSO__ENABLED false
KUBEN_SSO__ISSUER unset the provider’s issuer URL; its discovery document names the rest
KUBEN_SSO__CLIENT_ID unset
KUBEN_SSO__CLIENT_SECRET_FILE unset a file holding the client secret (preferred over client_secret)
KUBEN_SSO__CLIENT_SECRET unset the client secret itself
KUBEN_SSO__DISPLAY_NAME Single sign-on shown on the sign-in button
KUBEN_SSO__SCOPES ["openid", "email", "profile"]
KUBEN_SSO__GROUP_CLAIM groups the ID token claim that lists the person’s groups
KUBEN_SSO__GROUPS empty provider group → organization role (viewer … owner): {admins = "admin"}, or one variable per group, KUBEN_SSO__GROUPS__DEVS=developer
KUBEN_SSO__DEFAULT_ROLE unset role of people in no mapped group; unset refuses them
KUBEN_SSO__ALLOWED_DOMAINS empty (any) email domains allowed
KUBEN_SSO__REQUIRE_VERIFIED_EMAIL true
KUBEN_SSO__ORG bootstrap.org_slug the organization people join
KUBEN_SSO__DISABLE_PASSWORD_FOR_LINKED false accounts linked to the provider cannot sign in with a password
Variable Default Purpose
KUBEN_SECRETS__KEYRING_FILE secrets.keyring in the state directory, created with one key when missing the keyring of key-encryption keys; every replica must read the same file, and it must be backed up apart from the database. See Backup and restore