Skip to content

Configuration

All the environment variables the kubelatch-server binary reads, with their default value, format, and what they do. There is no config file or server flags.

At startup, kubelatch validates all the variables and, if there are errors, shows them all together and does not start. The server CLI subcommands read the same variables with the same rules: they also need DATABASE_URL, KUBELATCH_BASE_URL and KUBELATCH_ENCRYPTION_KEY.

Each variable has an anchor with its name in lowercase: configuration.md#kubelatch_base_url, configuration.md#database_url, configuration.md#github_app_id.

Common formats

Format What it accepts
Duration A number of days <n>d (no leading zeros, up to 3650d) or a Go duration greater than zero (720h, 90m), up to 10 years.
List Comma-separated values; spaces around them and empty items are ignored.
Boolean Exactly true or false. Any other value is an error.
URL Absolute, http or https, with no user, query (?) or fragment (#).

Server

Variable Required Default Format What it does
KUBELATCH_LISTEN No :8080 host:port Listen address and port. The Helm chart sets :8443 (:8080 with exposure: ingress).
KUBELATCH_BASE_URL Yes URL Public URL kubelatch is reached at. It is normalized: no trailing slash, scheme and host lowercased, no default port. Decides the Secure attribute of cookies (if https), the only trusted origin for cross-origin request protection, the URL of invitation links (<base>/account-link#kli_…), the server of the kubeconfigs (<base>/clusters/<id>), the audience (aud) GitHub Actions tokens must carry, the GitHub App callback (<base>/api/auth/github/callback) and the redirect URI of OIDC login (<base>/api/auth/oidc/callback).
KUBELATCH_ENCRYPTION_KEY Yes Standard Base64, exactly 32 bytes Master key. It protects the session cookies and each cluster's stored tokens. Rotating it closes every session and requires rotating each cluster's tokens.
KUBELATCH_INSTANCE_ID No default Kubernetes label value: alphanumeric, -, _, ., up to 63 characters Value of the kubelatch.io/instance label on the objects kubelatch creates in the clusters. That instance's cleanup and garbage collector only touch their own objects, which lets you move a cluster to another instance. It is not meant for two instances to share a cluster at the same time.
KUBELATCH_TRUSTED_PROXIES No empty List of CIDRs (10.0.0.1/32 for a single host) Networks from which X-Forwarded-For is trusted, read right to left up to the first untrusted hop. Affects the source IP recorded in the audit log and the control plane, and the per-IP limits on login, CLI sign-in, the CI exchange and proxy failures. A bare IP is an error, and so is a prefix wider than /8 (IPv4) or /16 (IPv6). Empty behind an L4 LoadBalancer; mandatory behind an Ingress.

To generate KUBELATCH_ENCRYPTION_KEY:

head -c32 /dev/urandom | base64

Database

Variable Required Default Format What it does
DATABASE_URL Yes Postgres connection string (postgres://user:password@host:5432/kubelatch?sslmode=require) Database holding all state. kubelatch applies its migrations at startup, and so does every CLI subcommand.

TLS

Variable Required Default Format What it does
KUBELATCH_TLS_CERT With KUBELATCH_TLS_KEY empty Path to a PEM file Server certificate. With both variables, kubelatch serves HTTPS (TLS 1.2 or higher, HTTP/2 and HTTP/1.1). Reloads the pair when it changes on disk (checks at most every 10 s); an invalid pair is ignored and logged. An invalid pair at startup prevents startup. Every hour it warns if fewer than 30 days of validity remain. Without them, kubelatch serves plain HTTP.
KUBELATCH_TLS_KEY With KUBELATCH_TLS_CERT empty Path to a PEM file Certificate's private key. The two variables go together: one without the other is an error.
KUBELATCH_KUBECONFIG_CA No empty Path to a regular PEM file, CERTIFICATE blocks only, up to 1 MiB CA embedded as certificate-authority-data in every kubeconfig it issues. Only needed with a private CA. A block that is not a certificate (a key pasted by mistake) prevents startup.
KUBELATCH_CLUSTER_CA_FILE No empty (the chart sets it) Absolute path to a PEM file The CA of the cluster kubelatch runs in, read each time it is needed. With it, enrolling that cluster with your identity needs nothing pasted. The chart mounts the kube-root-ca.crt ConfigMap there (clusterCA.mount). A missing file does not stop the start.

Permissions

Variable Required Default Format What it does
KUBELATCH_PROTECTED_NAMESPACES No empty List of namespace names Namespaces that admit no permission of any role, in addition to kube-system, kube-public, kube-node-lease and kubelatch-system, which are always protected. A name that is not a valid namespace name prevents startup.
KUBELATCH_REQUIRE_PSA No true Boolean With false, developer, debugger, admin and the custom roles that require it stop requiring the namespace to carry pod-security.kubernetes.io/enforce=baseline or restricted. Permissions warns about it. See System roles before changing it.

Limits and TTLs

Variable Required Default Format What it does
KUBELATCH_MAX_TTL_USER No 30d Duration Maximum lifetime of a person's credential.
KUBELATCH_MAX_TTL_BOT No 90d Duration Maximum lifetime of a bot's credential, including CI ones.
KUBELATCH_CLI_SESSION_TTL No 12h Duration, no greater than KUBELATCH_MAX_TTL_USER Lifetime of a kubelatch login session (see The CLI). If KUBELATCH_MAX_TTL_USER is shorter than 12 h, the default is that value.

Audit

Variable Required Default Format What it does
KUBELATCH_AUDIT_RETENTION No 90d Duration of at least 1 day, or 0 / 0d How long audit and control events are kept. A periodic job deletes older rows hourly. 0 or 0d turns that deletion off; the job still runs hourly to expire AI agent requests that nobody decided or ran in time, and join requests nobody answered. Join requests that were approved, denied, cancelled or expired are deleted once they are older than this value, as the events are. An instance that never had a license key keeps 24 hours at most, whatever this value says (0 included); see Editions and license. Spent CI token ids and expired CLI sign-in requests are purged by the job too, and on each CI exchange or new CLI sign-in, so they never depend on this value.

Edition

Variable Required Default Format What it does
KUBELATCH_LICENSE No empty A license key (a JWT) The Pro license key. Without it kubelatch runs as Free, with its limits (see Editions and license). The key is verified against the public keys built into the binary, at startup and every hour, with no network call. A key that does not verify leaves kubelatch in Free and shows a warning to administrators; it never prevents startup. When this variable is set, the key cannot be installed or removed from the interface. In Kubernetes it goes in the kubelatch-secrets Secret with the other secret variables.
KUBELATCH_ACCESS_CONTACT No empty A mailto: address, an https:// URL or a short text (at most 200 characters) Who a person asks for access. Home shows it beside Ask for access and Ask for an extension: as a link when it is a mailto: address or an https:// URL, and as text after the action when it is anything else. The 403 of a cluster names it too where a link to ask can't be given (Free). Empty: signed-in people get mailto: and the address of the oldest enabled administrator who has one (their own e-mail, or their OIDC identity's), and when no administrator has an address, Home leaves the sentences "Ask an administrator for access." and "Ask an administrator for an extension." without a link (its first steps name that administrator instead: "Ask your administrator (ana) for access.") and a 403 says "Ask your kubelatch administrator". With OIDC login, the login page shows it too when a sign-in finds no account ("To get an account, ask {contact}."), so GET /api/auth/methods returns it to anyone; the login page and GET /api/auth/methods only ever show the configured value, never an administrator's address.
KUBELATCH_TICKET_URL No empty An https:// URL with {key} once (at most 500 characters) Where a permission's ticket links to. A ticket is an https:// URL (always a link), a key such as INC-123 or ENG-42 (typed in any case, saved in capitals) or a number such as #512. A key or a number links through this template, with {key} replaced by the key or by the digits without #: https://acme.atlassian.net/browse/{key} (Jira), https://linear.app/acme/issue/{key} (Linear) or https://github.com/acme/ops/issues/{key} (GitHub Issues). Empty shows keys and numbers as text. Only https: is ever a link, and kubelatch does not check that the ticket exists. A value that is not an https:// URL with {key} once stops the start.
KUBELATCH_UPDATE_CHECK No true Boolean Within 30 seconds of starting and then once a day (an hour after a failed attempt), kubelatch fetches KUBELATCH_UPDATE_CHECK_URL to learn whether a newer version is published, and administrators see a notice in the bell and the Version row of Edition. The request carries no identifier, no version and nothing about the instance. false turns it off; a build without a version (dev) never checks.
KUBELATCH_UPDATE_CHECK_URL No https://docs.kubelatch.com/releases/latest.json An absolute http(s) URL Where the version check reads the latest published version ({"version", "published_at", "url"}). Point it at an internal mirror in an air-gapped installation. Of that document kubelatch reads version, and url only when it is an https link on this URL's own host: with a mirror that serves the documentation's file, the version shows without that link and How to upgrade leads to the upgrade guide on docs.kubelatch.com. A mirror URL with a user or password belongs in the Secret: the Helm chart refuses it in config.updateCheckURL, which lands in a ConfigMap.

E-mail

Variable Required Default Format What it does
KUBELATCH_RESEND_API_KEY No empty A Resend API key Lets kubelatch send e-mail through Resend: the invitation and reset links administrators would otherwise copy (e-mailed invitations), and the e-mail that warns each person with an address before one of their permissions ends, which they can turn off (notifications). Use a key with Sending access only, restricted to the sending domain. It is a secret: in Kubernetes it goes in the kubelatch-secrets Secret, and kubelatch never logs it. Without it, links are copied by hand and nobody gets e-mails; the other warnings keep working. It needs KUBELATCH_MAIL_FROM, and kubelatch refuses to start with one and not the other.
KUBELATCH_MAIL_FROM With KUBELATCH_RESEND_API_KEY empty One e-mail address, with or without a name, such as kubelatch <noreply@example.com> The sender of those e-mails, on a domain verified in Resend. A value that is not a single address stops startup. In the Helm chart it is mail.from.

Webhooks

Variable Required Default Format What it does
KUBELATCH_WEBHOOK_ALLOW_PRIVATE No false Boolean With true, the webhooks of Pro may reach addresses that aren't public: localhost, loopback, private (RFC 1918 and fc00::/7), link-local and the other reserved ranges, for a receiver inside your network. It lifts that check alone: the URL is still https only, redirects are still not followed, and HTTPS_PROXY is still ignored. In the Helm chart it is config.webhookAllowPrivate.

Logs

Variable Required Default Format What it does
KUBELATCH_LOG_LEVEL No info debug, info, warn or error, case-insensitive Lowest level kubelatch writes. debug adds a line for every /healthz and /readyz request.
KUBELATCH_LOG_FORMAT No json json or text, case-insensitive json writes one JSON object per line on standard output; text writes key=value pairs. What each line carries is in The server log.

Interface

Variable Required Default Format What it does
KUBELATCH_PROVIDER_LOGOS No published published, all or none, case-insensitive Which provider logos Clusters and the cluster page draw. published draws a set of them, all draws every provider's, and none draws the Kubernetes and k3s marks only. The provider's name is always shown in text.

CI

Variable Required Default Format What it does
KUBELATCH_CI_TOKEN_TTL No 1h Duration, no greater than KUBELATCH_MAX_TTL_BOT Lifetime of the credentials a GitHub Actions workflow gets when it exchanges its OIDC token.
KUBELATCH_GITHUB_ACTIONS_ISSUER No https://token.actions.githubusercontent.com URL; http only on a loopback host Issuer (iss) of GitHub Actions OIDC tokens. Its keys are read from <issuer>/.well-known/jwks. For GitHub Enterprise Server: https://<host>/_services/token.

AI agents

The MCP server for AI agents and the sign-in of delegated agents (AI agents).

Variable Required Default Format What it does
KUBELATCH_MCP_ENABLED No true true or false Serves the MCP server for AI agents at /mcp and the endpoints delegated agents sign in through (/oauth/…, /.well-known/…). With false they all answer 404.
KUBELATCH_MCP_RATE_LIMIT No 120 Number from 1 to 10000 Requests one credential may send to /mcp per minute; for a delegated agent, one session. Past it the answer is 429 until the minute ends. The count is kept by each replica.
KUBELATCH_AGENT_SESSION_TTL No 8h Duration, at least 5m Duration proposed when a person authorizes an AI agent to act on their behalf. At most KUBELATCH_AGENT_SESSION_MAX_TTL.
KUBELATCH_AGENT_SESSION_MAX_TTL No 24h Duration, at least 5m Longest duration a person may choose when authorizing an AI agent. At most KUBELATCH_MAX_TTL_USER.
KUBELATCH_AGENT_APPROVAL_WAIT No 50s Duration from 0 to 4m How long an AI agent's call waits for a person to decide a held write before it answers pending again. 0 answers at once. Keep it under the read timeout of any proxy in front of kubelatch.

Identity and GitHub

These variables configure login with your organization's GitHub App (see GitHub login). Without any of them, and without OIDC login, kubelatch works with local passwords only.

The five core variables (GITHUB_APP_ID, GITHUB_APP_CLIENT_ID, GITHUB_APP_CLIENT_SECRET, GITHUB_APP_PRIVATE_KEY, GITHUB_ORG) go together. With any of them present, or with GITHUB_WEBHOOK_SECRET, all five are required and kubelatch does not start if any is missing. GITHUB_REQUIRE_ORG_2FA, GITHUB_URL and GITHUB_API_URL alone activate nothing. All of them have surrounding spaces stripped.

Variable Required Default Format What it does
GITHUB_APP_ID With GitHub Positive integer App ID of the GitHub App. Signs the JWT kubelatch uses to get installation tokens.
GITHUB_APP_CLIENT_ID With GitHub Text (Iv1.…, Iv23…) App's Client ID, for OAuth login.
GITHUB_APP_CLIENT_SECRET With GitHub Text App's client secret, to exchange the login code server-side.
GITHUB_APP_PRIVATE_KEY With GitHub Full RSA PEM (PKCS#1 RSA PRIVATE KEY or PKCS#8 PRIVATE KEY), 2048 to 8192 bits; accepted on one line with literal \n App's private key. It is the file's content, not a path.
GITHUB_ORG With GitHub GitHub organization login (acme-corp) Only active members of this organization can sign in. The hourly member sync walks it.
GITHUB_WEBHOOK_SECRET No empty Text Activates POST /v1/github/webhook (HMAC-SHA256 signature in X-Hub-Signature-256), which instantly disables anyone who leaves the organization. Its presence turns on GitHub mode.
GITHUB_REQUIRE_ORG_2FA No true Boolean Rejects GitHub logins while kubelatch knows the organization does not require two-factor. It can only know this if the App has Organization → Administration: read; otherwise it lets people in and warns on every sync.
GITHUB_URL No https://github.com URL; http only on a loopback host Only for GitHub Enterprise Server: https://<host>. Base for the OAuth authorization routes.
GITHUB_API_URL No https://api.github.com URL; http only on a loopback host Only for GitHub Enterprise Server: https://<host>/api/v3.

Member sync runs every hour; that interval is not configurable.

OIDC login

These variables configure login with your company's identity provider over OpenID Connect: Google Workspace, Microsoft Entra ID, Okta, Keycloak, Authentik, Auth0 or any provider that publishes its discovery document (see OIDC login (SSO)). It can be on together with GitHub login. In the Helm chart they are the values under config.oidc, except OIDC_CLIENT_ID and OIDC_CLIENT_SECRET, which go in the Secret.

The four core variables (OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_ALLOWED_DOMAINS) turn it on. With any of them present, OIDC_ISSUER, OIDC_CLIENT_ID and OIDC_ALLOWED_DOMAINS are required, and kubelatch does not start while one is missing; the error names every missing one. So a stray OIDC_CLIENT_SECRET also stops the start. OIDC_DISPLAY_NAME, OIDC_SCOPES, OIDC_SUBJECT_CLAIM, OIDC_EMAIL_VERIFIED_CLAIM, OIDC_AUTOLINK and OIDC_ICON alone activate nothing. All of them have surrounding spaces stripped.

Variable Required Default Format What it does
OIDC_ISSUER With OIDC URL; http only on a loopback host; kept as written, trailing slash included The provider's issuer, exactly as its discovery document has it, with its trailing slash if it has one (Auth0 and Authentik end in /). kubelatch reads <OIDC_ISSUER>/.well-known/openid-configuration and accepts only ID tokens whose iss is this value. A Microsoft Entra ID issuer (login.microsoftonline.com, login.microsoftonline.us, login.partner.microsoftonline.cn or sts.windows.net) must carry the tenant id, a GUID: with common, organizations, consumers or a domain name, kubelatch does not start.
OIDC_CLIENT_ID With OIDC Text Client id of the application registered in the provider. ID tokens must name it in aud.
OIDC_CLIENT_SECRET No empty Text Client secret of a confidential client, to exchange the login code server-side. Empty means a public client, with PKCE only.
OIDC_ALLOWED_DOMAINS With OIDC List of domain names (acme.com, acme.org), at least one The e-mail domains accepted, lowercased and exact: a subdomain has to be listed on its own, and wildcards are refused. A sign-in with an address of any other domain is refused.
OIDC_DISPLAY_NAME No SSO Text, 1 to 40 characters, no control characters The provider's name in the interface: the button reads Sign in with {name}, and the messages of the login page name it.
OIDC_ICON No deduced from OIDC_ISSUER google, microsoft, okta, auth0 or generic, case-insensitive The logo on the sign-in button. Empty, kubelatch recognises the provider from the issuer's host: Google, Microsoft Entra ID, Okta and Auth0. Any other issuer gets a generic key. Set it for an issuer on your own domain, such as sso.acme.com in front of Okta. With any other value, kubelatch does not start.
OIDC_SCOPES No openid email profile Scopes separated by spaces or commas The scopes kubelatch asks for. Without openid or email, kubelatch does not start.
OIDC_SUBJECT_CLAIM No sub Claim name: a letter or _, then letters, digits, _, ., : or -, up to 64 characters The claim that identifies a person. It must name a claim that belongs to one person and never changes or passes to someone else: sub, or oid for Microsoft Entra ID. With a claim that holds an address or a name (email, upn, preferred_username, name…), one that many people share (tid, hd, iss, aud, idp, locale…), one of the token or the session (nonce, sid, jti…) or the claim of OIDC_EMAIL_VERIFIED_CLAIM, kubelatch does not start. It is part of the identity, like OIDC_ISSUER: after a change to either, each person's next sign-in replaces their identity by e-mail, as OIDC_AUTOLINK allows (Changing the provider or the subject claim).
OIDC_EMAIL_VERIFIED_CLAIM No email_verified Claim name, same format The claim that says the provider verified the e-mail address; it must be true. Microsoft Entra ID uses xms_edov.
OIDC_AUTOLINK No all all, invited or off, case-insensitive Which accounts a first sign-in links by their e-mail address: all, any enabled account with that address; invited, only one that never signed in or has a pending invitation or reset link; off, none, and each account is linked with its invitation or linking link (The link policy).

What goes in a Secret

In Kubernetes, DATABASE_URL, KUBELATCH_ENCRYPTION_KEY, KUBELATCH_LICENSE, KUBELATCH_RESEND_API_KEY, the sensitive GITHUB_* variables, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET go in the kubelatch-secrets Secret, which the Deployment loads whole. Everything else goes in the chart's config values, which become the kubelatch-config ConfigMap (with the suggested release name, kubelatch): see Helm values.