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¶
To generate KUBELATCH_ENCRYPTION_KEY:
head -c32 /dev/urandom | base64
Database¶
TLS¶
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¶
CI¶
AI agents¶
The MCP server for AI agents and the sign-in of delegated agents (AI agents).
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.
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.