Skip to main content

Values reference

This page answers three questions that Configuration Reference does not, because it is organised by component rather than by decision:

  1. Which values do I have to set, and does that depend on how I am deploying?
  2. What does a complete values file look like, and which Secrets does it assume already exist?
  3. What do I change before I go live, and what breaks if I do not?

Use this page to write your values file. Use Configuration Reference to look up what an individual setting does.

What you are looking at​

charts/align/values.yaml in the chart holds 28 top-level keys and 155 values whose default is an empty string. An empty default is a decision left to you, so those 155 are the set this page exists to triage: which are mandatory, which are mandatory only in some deployment shapes, which you can leave alone, and which do nothing at all in the current chart.

Both figures are re-derivable against the chart you were shipped, and both are compared against it in CI, so a value added to the chart without a line on this page fails the build:

# 28
grep -cE '^[a-zA-Z_][a-zA-Z0-9_]*:' charts/align/values.yaml
# 155
grep -vE '^[[:space:]]*#' charts/align/values.yaml | grep -cE ':[[:space:]]*""'

What those counts exclude: keys whose default is an empty list ([]), an empty map ({}) or false. An empty list or map is usually "add nothing and it works"; a false is a feature switch. Neither leaves you guessing whether the install will start, which is what the 155 do.

They also exclude two things worth knowing about:

  • Values with a real default that still matter. gateway.trustedProxyHops, gateway.schemaCheckMode and networkPolicies.enabled all ship with a value and all appear in the go-live checklist below.
  • Settings the chart's templates read that values.yaml does not declare. There are several, gateway.apiBaseUrl and gateway.authDemoMode among them. They work when you set them and they are invisible to any sweep of the values file, so the ones that matter are named explicitly on this page.
tip

Your onboarding pack ships a values file we have validated against your registry and your cluster shape. This page is that file explained, not a replacement for it. If the two disagree, the pack is the one we tested.

1. Before the values file: the Secrets​

This comes first because it is what stops a first install. The recommended production setting is secrets.create: false, which means the chart creates no Secrets at all and every secrets.* value in your values file is ignored. That is the right choice, because a values file ends up in git. It also means the Secrets have to exist before you run helm install, and a missing one does not produce a log to read: the pod never starts, and kubectl describe pod says CreateContainerConfigError.

These are the Secret keys a default install references without optional: true, so each one must exist:

SecretKeys that must existWhen
The name you put in database.secretName (align-database by default)url, host, username, passwordAlways
align-internaljwt-secret, cookie-secret, encryption-key, service-auth-tokenAlways
align-internalmaster-encryption-keyAlways in practice. It is optional: true at the Kubernetes layer, so the pod starts, and then the gateway refuses to boot because token encryption has no key. Installation creates it in Step 3's Minimal Secrets.
align-oidcissuer-url, client-id, client-secretgateway.oidc.enabled is true
The name you put in gateway.redis.secretNamethe key you put in gateway.redis.secretKeyYou set secretName rather than an inline URL
align-llmopenai-api-key or anthropic-api-keyYou use a hosted model

Two things about those names. The internal, OAuth, LLM and Cognito Secrets are named after the release: align-internal is what you get from helm install align, and helm install foo wants foo-align-internal. The database Secret is the exception, because you name it yourself in database.secretName.

app-url in the database Secret is optional: true and cannot exist at install time: the align_app role is created by a migration, so there is no password to put there until the first install has run. Add it during the RLS cutover. Until then the gateway connects as the owner, which is the go-live item below.

Installation has the kubectl create secret commands. Secrets covers sealed secrets and External Secrets Operator.

2. Required values, by deployment shape​

Every install​

ValueWhyWhat happens if you skip it
global.imageRegistryThe registry root your images come fromThe chart refuses to render, naming the image it would have produced. An empty value gives a bare image name, and Docker resolves a bare name against Docker Hub rather than anywhere Align controls. (global.allowUnqualifiedImages: true disables that refusal. Do not set it.)
global.imageTagPins the release you testedResolves to latest, which is not a release. A pod restart can pull a different build than the one you validated.
database.secretNameNames the Secret holding your database credentialsDefaults to align-database. If no Secret by that name exists, no pod starts: CreateContainerConfigError, and no container logs to read.
gateway.signupClosedMessageWhat a person is told when they first sign in with a work email and your organisation has no workspace for that domainOptional. Empty shows a neutral message that asks them to contact their workspace administrator. Set it to name your own contact.
gateway.sso.groupRoleMappingJSON that maps a directory group name to org_admin, member or viewerOptional. Empty maps no group, so every SSO user is a member. Renders SSO_GROUP_ROLE_MAPPING.
gateway.sso.groupsClaimThe token claim that lists a person's groupsOptional. Empty uses groups. Renders SSO_GROUPS_CLAIM.
gateway.sso.groupFunctionMappingJSON that maps a directory group name to an onboarding work functionOptional. Only pre-fills a hint, never a role. Renders SSO_GROUP_FUNCTION_MAPPING.
gateway.saml.enabledTurns on SAML 2.0 sign-inDefault false: no /saml route exists. Needs gateway.apiBaseUrl.
gateway.saml.spEntityIdThe service provider entity idOptional. Empty uses <apiBaseUrl>/saml/metadata.
gateway.saml.spAcsUrlThe assertion consumer service URLOptional. Empty uses <apiBaseUrl>/saml/acs. The IdP checks it exactly.
gateway.saml.groupsAttributeThe assertion attribute that lists a person's groupsOptional. Empty uses groups.
gateway.saml.defaultTenantIdThe tenant uuid the SAML sign-in button and /saml/login use when no tenant is namedSet it on a self-host that wants the SAML sign-in button and the SAML admin screen. Empty (the default): no SAML button, /saml/login with no ?tenant= stays 400, and enforcing SSO with SAML as the only provider is refused. Must be a UUID; the chart fails the render otherwise.
gateway.oauthAuthorizationServer.issuerUrlgateway.oauthAuthorizationServer.enabled is trueThe chart refuses to render. Set it to the public API URL, with no trailing slash. It is the OAuth issuer that Claude and ChatGPT discover, so it must be reachable from the internet over https.
gateway.frontendUrlWhere a human opens Align: decision links, magic-link emails, OAuth and SSO redirects, the cookie domainSee its row in the go-live checklist. Leave it empty only when ui.ingress.enabled is true and its first host is the URL your users type. Otherwise you get http://align.local and every link is broken.

More than one gateway replica​

Scaling the gateway out needs a shared session store, and this is a security property rather than a performance one.

ValueWhyWhat happens if you skip it
gateway.redis.url, or gateway.redis.secretName plus gateway.redis.secretKeyThe session-revocation marker lives in the cache backendThe chart refuses to render gateway.replicaCount above 1, and refuses an autoscaler whose maxReplicas is above 1. Without the refusal, a session revoked through the admin API would be revoked only on the pod that served that call and would stay live on every other pod until the token's own 7-day expiry. No pod's marker survives a restart either, so revocation is not durable at any replica count without a shared store.
gateway.allowUnsharedSessionStoreOpts out of that refusalOff by default, and there is no good reason to turn it on for an install reachable from the internet.

Any Redis-compatible service works: ElastiCache, Memorystore, Azure Cache, Dragonfly, KeyDB. Supplying the URL through gateway.extraEnv as REDIS_URL also satisfies the check, because the deployment renders extraEnv verbatim.

brain.replicaCount and ui.replicaCount default to 2 and need nothing extra. Neither holds per-pod session state.

On disruption budgets, since the defaults are asymmetric: gateway.pdb.enabled, brain.pdb and ui.pdb are on, and the gateway's is skipped rather than refused below two replicas, so a single-replica install is still drainable. Connectors have no pdb key at all and default to off, so an enabled connector has no eviction protection until you add one. A connector PDB at one replica is refused, because minAvailable: 1 against a single pod blocks every voluntary eviction, including a node drain.

Single sign-on​

Pick one of the two paths. In production the gateway does not start with neither. See No identity provider for email sign-in.

PathValues
Your own identity provider (Azure AD, Okta, Keycloak, any OIDC)gateway.oidc.enabled, plus gateway.oidc.scopes if you need something other than openid email profile, plus gateway.oidc.defaultTenantId for a single-tenant install. Credentials go in the align-oidc Secret, not in values.
Cognito, where we host the user poolgateway.cognito.googleIdpName and gateway.cognito.azureIdpName only if your pool's identity provider names differ from Google and Microsoft. Empty means the code defaults apply.

Social and enterprise sign-in credentials, if you use them, are secrets.oauth.google.clientId, secrets.oauth.google.clientSecret, secrets.oauth.azure.clientId, secrets.oauth.azure.clientSecret and secrets.oauth.azure.tenantId. See OAuth Apps.

Connectors you turn on​

Each connector needs its own OAuth credentials, and an enabled connector without them starts and then fails at the point a user tries to connect it, not at install. One provider app can cover two connectors: a single Atlassian app serves both Jira and Confluence. The credential table below is the authority on which values each one needs. The connector switches are connectors.slack.enabled, connectors.teams.enabled, connectors.jira.enabled, connectors.github.enabled, connectors.confluence.enabled, connectors.zoom.enabled, connectors.linear.enabled, connectors.azureDevOps.enabled, connectors.m365Documents.enabled and connectors.align.enabled. Azure DevOps and M365 Documents default to true.

Redirect URIs come from gateway.apiBaseUrl, which the chart reads and values.yaml does not declare. Every connector's callback URL is derived from it, falling back to your gateway.ingress host. Set it when those differ, and register the exact derived URL in each provider app: see OAuth Apps.

The same value is also the gateway's public address, rendered as the PUBLIC_GATEWAY_URL environment variable, but only when you named a real address: gateway.apiBaseUrl, or gateway.ingress.enabled: true with your host. A default install renders nothing, so org invite links carry no gw= parameter and share approval stays closed until you set one. Two things read it. Approving a share needs it, and org invite links carry it as gw= so that align join '<link>' knows which server it is joining. What is published is the origin only, so it must be https (plain http is accepted only for localhost) with no user name, path, query or fragment. A value that breaks those rules does not stop the pod: the gateway logs a warning naming PUBLIC_GATEWAY_URL, invite links carry no gw= and invite-peek names no gateway. The one exception is production with SHARE_APPROVAL_MODE=required, where it refuses to start. FRONTEND_URL, from gateway.frontendUrl, is the web address the same links point at, so it has to be the URL your users type.

connectors.align is the MCP endpoint your agents talk to. If you expose it, it needs connectors.align.ingress.className and a host. Set connectors.align.defaultTenantId for a single-tenant install: left empty it does not mean "no tenant", it means the fixed tenant id 00000000-0000-0000-0000-000000000001.

Secrets held in a cloud secret manager​

externalSecrets.enabled turns on External Secrets Operator integration, which templates ExternalSecret objects for the database, internal, LLM and Slack OAuth Secrets. It is not a drop-in replacement for pre-creating all of them. ESO must already be installed.

The fields the chart actually reads are externalSecrets.secretStore.name, externalSecrets.secretStore.kind, externalSecrets.refreshInterval and externalSecrets.aws.secretPath, and the last one is used as the remote key prefix for every provider, not just AWS. The per-provider fields are read by no template today: see section 5. See Secrets.

An LLM that is not a cloud API​

secrets.llm.openaiApiKey and secrets.llm.anthropicApiKey are how the brain reaches a hosted model. To run inference in your own cluster instead, use gpu.devicePlugin.enabled and gpu.llmServer.enabled, with gpu.llmServer.quantization and gpu.llmServer.maxModelLen if the model needs them. See LLM Configuration.

If you run a model in your cluster to keep inference inside your network, note that secrets.llm.openaiApiKey is also what sends embedding text to OpenAI. Natural-language search needs it.

Behind an outbound proxy​

global.proxy.httpProxy, global.proxy.httpsProxy and global.proxy.noProxy reach the gateway, the brain, the UI and every connector, which is where outbound calls to Slack, Jira, GitHub and a model API come from. Put your in-cluster service names and the cluster DNS suffix in noProxy, or pod-to-pod calls go out through the proxy and fail.

They do not reach the migrations job, the telemetry CronJobs, the OpenTelemetry collector or the GPU inference server: those templates do not include the proxy helper. If your network requires every egress to go through the proxy, those four are the ones to check by hand.

The chart brings its own Postgres, Redis and secrets (pilots)​

postgresql, redis and rlsCutover are three opt-in switches, all off by default. They are for a pilot or a single-VM install, not for production. The walk-through is in Installation.

  • postgresql.enabled runs a single-replica pgvector StatefulSet and writes the database Secret. It refuses to render without rlsCutover.enabled, and refuses to overwrite an existing database Secret it did not create. It is created once, at the first install, and uninstalling the release leaves it behind.
  • redis.enabled runs a password-protected Redis and wires it into the gateway, which satisfies the more-than-one-replica rule above. gateway.redis.url and gateway.redis.secretName still win.
  • rlsCutover.enabled sets the align_app password after migrations and checks the connection before the gateway starts. It needs app-password and app-url in the database Secret, which postgresql.enabled writes.
  • With secrets.create: true the chart generates master-encryption-key when you give none, and keeps it across upgrades. Back it up.

Generated passwords and keys are read back from the cluster, so these switches need the Helm CLI. Under a GitOps controller, or when you only render the chart, set the passwords and the master key yourself.

Database TLS​

Use global.rdsSSL.enabled. It mounts the RDS CA bundle and points the gateway and the brain at it, and despite its name it is the only implemented switch.

global.databaseSSL.enabled, global.databaseSSL.provider, global.databaseSSL.customCA.secretName and global.databaseSSL.customCA.secretKey are read by no template in the current chart, so setting them has no effect and no custom CA bundle is ever mounted. Use global.rdsSSL.enabled.

3. A complete starter values.yaml​

This is a whole file, not a fragment. Every line you have to decide is either filled in or marked. Copy it, replace every REPLACE_ME, delete the sections you do not need, and create the Secrets from section 1 first.

# ---------------------------------------------------------------------------
# Align, self-hosted. A complete starter values file.
#
# Replace every REPLACE_ME. Anything not listed here keeps its chart default;
# see /self-host/configuration for what each default is. Values the current
# chart reads NOWHERE are deliberately left out of this file - they are listed
# on the page instead, so nobody sets one and expects an effect.
# ---------------------------------------------------------------------------

global:
environment: production

# REQUIRED. The registry ROOT, without the `align/` path segment the chart
# already carries in each image repository. The chart refuses to render if this
# is empty.
# Align registry: the $ALIGN_REGISTRY host issued at onboarding
# AWS ECR: 123456789.dkr.ecr.us-east-1.amazonaws.com
# Azure ACR: myregistry.azurecr.io
# GCP AR: us-docker.pkg.dev/my-project
imageRegistry: REPLACE_ME

# Pin the release you tested. Empty means "latest", which is not a release.
#
# This does NOT reach the connectors. Each one carries its own `image.tag: latest`
# in values.yaml, and a per-component tag wins over the global one, so a
# connector keeps pulling `latest` however you set this. Pin each enabled
# connector explicitly below.
imageTag: REPLACE_ME

# The docker-registry Secret you created. /self-host/install calls these
# ecr-credentials / acr-credentials / gcr-credentials - use the name you
# actually created.
imagePullSecrets:
- name: REPLACE_ME

# Mounts the RDS CA bundle. The only implemented database-TLS switch; the
# global.databaseSSL block in values.yaml is read by no template.
rdsSSL:
enabled: true

# Outbound HTTP proxy, if your cluster has no direct egress. Put in-cluster
# names and the cluster DNS suffix in noProxy or pod-to-pod traffic goes
# through the proxy and fails.
proxy:
httpProxy: ""
httpsProxy: ""
noProxy: ""

# The Secret you created with database credentials. Keys url, host, username and
# password must exist. app-url comes later, at the RLS cutover.
database:
secretName: align-database

serviceAccount:
create: true
name: align
annotations: {}

# Self-host licensing. Set enabled: true once we have issued your license and you
# have created the Secret named below - see /self-host/licensing.
license:
enabled: false
secretName: align-license
secretKey: license.jwt
# The daily licence check-in (see /self-host/licensing). false = no call.
checkin:
enabled: true
url: ""

gateway:
enabled: true

# Above 1 you MUST set gateway.redis below. The chart refuses the combination.
replicaCount: 2
redis:
# Either an inline URL, or a Secret. Any Redis-compatible service.
url: ""
secretName: align-redis
secretKey: url

# How many proxy hops are in front of the gateway, as TRUSTED_PROXY_HOPS. One
# ingress is 1. An ingress behind a CDN is 2. request.ip, and therefore every
# address in the audit log, resolves through this. Count them and match. The
# chart caps it at 10. Do not write a leading zero: unquoted `010` is YAML octal
# and reaches the gateway as 8, which the chart CANNOT catch because YAML parses
# it before any template runs. Only the quoted form (`"08"`) is refused.
trustedProxyHops: 1

# Where humans open Align. Derived from ui.ingress ONLY when that ingress is
# enabled and has a host; otherwise it falls back to http://align.local and
# every link breaks. Set it explicitly unless you are sure.
frontendUrl: https://align.REPLACE_ME

# Read by the chart, absent from values.yaml. Every connector's OAuth redirect
# URI is derived from this. Set it when the API is reached at a URL other than
# the ingress host below, and register the derived URIs in each provider app.
# apiBaseUrl: https://api.align.REPLACE_ME

ingress:
enabled: true
className: nginx
hosts:
- host: api.align.REPLACE_ME
paths:
- path: /
pathType: Prefix
tls:
- secretName: api-align-tls
hosts:
- api.align.REPLACE_ME

# Keep `enforce`. It refuses to boot when the database is behind the migrations
# this build requires, which is the condition that otherwise corrupts writes.
# If you ever need the escape hatch, `"off"` must be QUOTED: bare off is a YAML
# boolean and the chart refuses it rather than silently ignoring you.
schemaCheckMode: enforce

# Distributed job processing is selected by the PRESENCE of these queue URLs,
# not by a provider name. With none set, Discover jobs run on an in-process
# queue and queued work is lost on a pod restart.
sqs:
importJobQueueUrl: ""
bulkApprovalQueueUrl: ""
bulkDeleteQueueUrl: ""

# Your own identity provider. Credentials go in the align-oidc Secret.
oidc:
enabled: true
scopes: ""
defaultTenantId: ""

# Only if you use the Cognito path AND your pool's provider names differ from
# the code defaults (Google / Microsoft).
cognito:
googleIdpName: ""
azureIdpName: ""

# Connection pooling sidecar. Turn on for high load rather than day one.
pgbouncer:
enabled: false

# Per-component image tag. Leave empty so it follows global.imageTag.
image:
tag: ""

# Cloud access for the gateway specifically (IRSA, workload identity).
serviceAccount:
create: false
name: ""

# Anything without a first-class value. Keep this short: a setting that belongs
# in a review conversation belongs in a named value instead. Do NOT put
# TRUSTED_PROXY_HOPS here as well - the chart refuses the pair, because
# extraEnv renders last and would silently win.
extraEnv: []

brain:
enabled: true
replicaCount: 2
image:
tag: ""

ui:
enabled: true
replicaCount: 2
image:
tag: ""
ingress:
enabled: true
className: nginx
hosts:
- host: align.REPLACE_ME
paths:
- path: /
pathType: Prefix
tls:
- secretName: align-tls
hosts:
- align.REPLACE_ME

# Turn off what you are not using. An enabled connector with no OAuth credentials
# starts, and fails when a user tries to connect it.
#
# PIN THE TAG on every connector you enable. Unlike the other components, each
# connector ships `image.tag: latest` in values.yaml, and a per-component tag beats
# global.imageTag - so leaving this out means a pod restart can pull a connector
# build you never tested, whatever you pinned globally. Use the same release.
connectors:
slack:
enabled: true
image:
tag: REPLACE_ME
teams:
enabled: false
jira:
enabled: true
image:
tag: REPLACE_ME
github:
enabled: true
image:
tag: REPLACE_ME
confluence:
enabled: false
zoom:
enabled: false
linear:
enabled: false
# The MCP endpoint your coding agents talk to. An empty defaultTenantId means
# the fixed id 00000000-0000-0000-0000-000000000001, not "no tenant".
align:
enabled: true
defaultTenantId: ""
image:
tag: REPLACE_ME
ingress:
enabled: false
className: ""
hosts: []
tls: []

# `create: false` is the production path and means the chart creates NO Secrets.
# Every secrets.* value below is then ignored, including the master key - supply
# these through the Secrets in section 1 instead. The block is here so you can
# see what exists, not so you can fill it in.
secrets:
create: false

internal:
# Ignored while create is false. The real home for this is the
# master-encryption-key key of the align-internal Secret.
masterEncryptionKey: ""
jwtSecret: ""
cookieSecret: ""
encryptionKey: ""
serviceAuthToken: ""
mcpAlignToken: ""

# One block per connector you enabled. See /self-host/oauth-apps. Zoom has no
# values.yaml block at all: the gateway reads its credentials from the
# align-oauth-zoom Secret (client-id, client-secret, webhook-secret, and robot-jid
# for Team Chat).
oauth:
slack:
clientId: ""
clientSecret: ""
signingSecret: ""
slackPersonal:
clientId: ""
clientSecret: ""
github:
clientId: ""
clientSecret: ""
appId: ""
appPrivateKey: ""
appClientId: ""
appClientSecret: ""
personalClientId: ""
personalClientSecret: ""
atlassian:
clientId: ""
clientSecret: ""
linear:
clientId: ""
clientSecret: ""
gitlab:
clientId: ""
clientSecret: ""
notion:
clientId: ""
clientSecret: ""
teams:
clientId: ""
clientSecret: ""
botAppId: ""
botAppPassword: ""
botAppTenantId: ""
google:
clientId: ""
clientSecret: ""
azure:
clientId: ""
clientSecret: ""
tenantId: ""

llm:
existingSecret: ""
openaiApiKey: ""
anthropicApiKey: ""

# In-cluster TLS certificate generation, for local development. TLS normally
# terminates at your ingress.
tls:
create: false
secretName: ""
commonName: ""
altNames: []

# Align's hosted billing. No effect in a self-hosted install.
stripe:
secretKey: ""
webhookSecret: ""
priceIdTeam: ""

# External Secrets Operator. Only secretStore.name, secretStore.kind,
# refreshInterval and aws.secretPath are read by the chart, and aws.secretPath is
# the remote key prefix for every provider rather than just AWS. The
# per-provider fields (provider, aws.region, azure.vaultUrl, azure.tenantId,
# gcp.projectId, vault.server) are read by no template today, so they are left
# out of this file.
externalSecrets:
enabled: false
secretStore:
name: ""
kind: ClusterSecretStore
refreshInterval: 1h
aws:
secretPath: align/production

# Usage telemetry into YOUR database. Nothing leaves your cluster.
# telemetry.export.prometheus and telemetry.export.datadog are read by no
# template today.
telemetry:
enabled: true

# Database migrations run as a Helm pre-upgrade hook. The image tag must match
# the gateway's or the database ends up behind the application; the chart
# enforces that lockstep.
migrations:
enabled: true
image:
tag: ""
# Upper bound on the migrations hook Job. See the go-live row.
activeDeadlineSeconds: 900

# Default-deny network posture. Only enforced under a policy-capable CNI.
# Validate enforcement on a non-production cluster first. See /self-host/network.
networkPolicies:
enabled: false
ingress:
fromIngressController:
enabled: false
allowedIngressCIDRs: []
allowedExternalCIDRs: []
allowDNS: true

# Prometheus Operator PodMonitor for infrastructure metrics.
podMonitor:
enabled: false

# Non-root, read-only-root-filesystem pod security context. Keep it on.
securityContext:
enabled: true

# In-cluster GPU inference instead of a cloud model API. See /self-host/llm.
gpu:
devicePlugin:
enabled: false
llmServer:
enabled: false
quantization: ""
maxModelLen: ""

# OpenTelemetry tracing. Off by default. Enabling runs a collector in your
# cluster. The credential is NOT taken from values: the collector reads it from a
# Secret named align-otel, and that reference is required, so enabling a backend
# without the Secret gives you a collector pod that will not start.
observability:
enabled: false
grafanaCloud:
enabled: false
otlpEndpoint: ""
authToken: ""
datadog:
enabled: false
apiKey: ""
site: datadoghq.com
prometheus:
enabled: false

# Cluster placement, applied to every Align pod.
nodeSelector: {}
tolerations: []
affinity: {}

# What a connector captures for a tenant with no capture setting of its own. off saves nothing until
# someone opts a connector in. propose saves only what a person confirms. auto leaves each connector's
# own default.
capture:
defaultMode: "off" # keep the quotes: unquoted, YAML reads off as false

4. Go-live checklist​

These are the chart defaults we would raise in your security review ourselves. Each row names the failure mode, not just the recommendation, because every one of them fails quietly.

SettingChart defaultDo this before go-liveWhat goes wrong if you do not
networkPolicies.enabledfalse, so an install that changes nothing has no default-deny policyConfirm your CNI enforces NetworkPolicy, then turn this on, then confirm pod-to-pod traffic still flows on a non-production cluster firstA cluster whose CNI ignores NetworkPolicy accepts the objects silently. You get the appearance of segmentation and none of it. On EKS with the AWS VPC CNI that means the node agent running with network policy enabled.
The align_app database roleThe application connects as the database ownerComplete the RLS cutover and add app-url to the database SecretIn PostgreSQL a table owner bypasses row-level security by design. The policies are present, FORCE-enabled and tested, and they are not yet the operative boundary. Application-layer tenant scoping is holding the line alone until you cut over.
gateway.trustedProxyHops, which sets TRUSTED_PROXY_HOPS1, which assumes one proxy in front of the gatewayCount the proxies actually in front of it and match. One ingress is 1. An ingress behind a CDN is 2.Too low and your audit log records your own ingress rather than the caller. Too high and a caller can forge their own address by prepending an x-forwarded-for entry. It is a number to match reality, not one to raise for safety. Neither direction errors. The chart caps it at 10 and refuses a leading zero, but within that range nothing can tell a wrong count from a right one.
The token encryption key (master-encryption-key in the align-internal Secret)No default. None is generated unless secrets.create is true, which generates one and keeps itGenerate it, store it in your secret manager, plan rotationThe gateway refuses to boot, so this one fails loudly unless gateway.authDemoMode is true. Rotation needs no downtime but does need planning: Key Rotation.
gateway.authDemoModeUnset, and absent from values.yamlNever set it in a real installThe chart reads it even though the values file does not list it. Setting it permits storing OAuth tokens in plaintext and turns the boot refusal above into a warning in the log. It is the most security-relevant value an operator can set by accident.
gateway.redis.url or gateway.redis.secretName, above one replicaBoth emptySet one before raising gateway.replicaCountThe chart refuses the combination, so this also fails loudly. The quiet failure is gateway.allowUnsharedSessionStore: true, which turns the refusal off and leaves session revocation per-pod and not durable across a restart.
gateway.frontendUrlEmpty, derived from ui.ingressSet it explicitly, or confirm ui.ingress.enabled is true and its first host is the URL your users typeWith no ingress host to derive from it becomes http://align.local. Every decision link, magic-link email and OAuth redirect points there, and nothing errors. This bites exactly the installs that front the UI with something other than the chart's Ingress.
gateway.schemaCheckModeenforceLeave itwarn and off let the gateway serve against a database behind the migrations this build requires, which corrupts writes with nothing failing.
gateway.serviceTenantBinding"log"Leave it, and read its log lines before any upgrade that turns it to enforceIt decides whether a connector's service token may name a tenant that connector does not serve. log refuses nothing and writes one [serviceTenantBinding] would refuse line per request it would refuse. enforce refuses those requests from the Slack, Teams, GitHub and mcp-align tokens with 403. off stops checking. A bare off must be QUOTED, and any other value fails the render.
gateway.serviceTokenPinnedTenantIdEmptyOn a single-tenant install, set it to your tenant idWith it set, the shared service token is checked against that one tenant rather than "any tenant with a connection". Today that check only logs. Leave it empty on a multi-tenant install, or every other tenant's shared-token traffic logs as a would-be refusal.
migrations.activeDeadlineSeconds900Leave it, unless a release ships a migration you expect to run longer. Then raise it for that upgrade.Lowered too far, a slow migration is killed mid-run and the upgrade fails. Raised far beyond your longest migration, a migrations pod that can never start (a missing ServiceAccount, an unpullable image) holds the upgrade open that long before it fails. The chart refuses zero, a negative value or null, so the hook is always bounded. Align's own migration hook runs in about 13 seconds.
global.imageTagEmpty, resolving to latestPin the release you testedlatest is not a release. A pod restart can pull a different build than the one you validated, and you cannot say afterwards which one ran.
connectors.<name>.image.taglatest, explicitly, for every connectorPin each enabled connector to the same releaseThis is the trap inside the row above. A per-component tag beats global.imageTag, and every connector carries image.tag: latest in the shipped values file, so pinning the global tag leaves every connector on a floating tag. Verified by rendering the chart: global.imageTag: v9.9.9 produces align/gateway:v9.9.9 and align/connector-slack:latest in the same install.
gateway.sqs.importJobQueueUrl and its two siblingsEmptySet them if you want distributed job processingWith none set, Discover work runs on an in-process queue and queued jobs are lost on a pod restart, with no error and no retry.
securityContext.enabledtrueLeave itTurning it off drops the non-root user and the read-only root filesystem.
global.allowUnqualifiedImagesfalseLeave itIt disables the registry refusal in the first row, so an unqualified image name resolves against Docker Hub.

Five things the checklist cannot cover, stated here rather than left out:

  • In-cluster traffic is plain HTTP. Gateway to brain and gateway to connector calls are unencrypted inside your cluster and authenticated with a shared service token. TLS terminates at your ingress. Mutual TLS is documented guidance, not shipped on by default, and the default-deny NetworkPolicy above is off until you enable it.
  • A historical import holds raw content for at least 33 days. Batch rows keep the full fetched items so a long import can resume. They purge 33 days after the job last changed, for jobs that completed or failed. A cancelled job is never purged. See Data Retention.
  • The raw message discard is best effort, not a guarantee. Raw text rides inside the decision row until analysis finishes and is blanked then. If analysis dies mid-run the copy persists until someone notices.
  • Embeddings are not covered by PII redaction. The redaction choke point covers the completion, stream and tool-call paths. The embeddings path sits outside it.
  • All of the above stays in your own database. None of it reaches Align.

First scan: name the sources, see a sample (scan.requirePreview)​

scan.requirePreview defaults to true, so a self-hosted install enforces this: before the first scan of a connector you name the sources in scope, and Align shows you a sample of what it would read before it reads it. Three things are worth knowing before you rely on it.

  • What the sample is. A bounded list of titles and links for the sources you named, at most 10 per source and at most 20 sources, plus a count. For Slack it is a message count and no titles. It never shows message or page text, makes no model call and stores nothing. Calling POST /import/jobs/preview is what shows it.
  • What it gates. The first scan is refused with a 409 until a sample for exactly the same sources has been shown to the same person (it is remembered for an hour). If the gateway cannot record or read that, the scan is refused with a 503, never allowed. It also stops a first scan from starting on its own, since nobody named its sources. It holds the first-scan screen, POST /import/first-scan, and the Discover routes that start a scan: POST /import/jobs, POST /import/jobs/bulk-start and POST /import/jobs/:jobId/start (a first-scan job is not asked again at start, it passed when it was made). A Discover scan of a previewable connector therefore needs named sources too. The preview route allows 30 previews per tenant per minute, and answers 503 if it cannot count them. POST /import/jobs/dry-run, the cost estimate, and a retry of a job that already ran are not held to the preview.
  • What it covers. Slack, GitHub, Confluence, Jira (Cloud and Data Center, see below) and Notion. For Notion you do not name pages in Align. The scope is whatever you share with the Notion app on Notion's own consent screen, and the sample lists what is shared. Teams, Linear, Zoom and Azure DevOps have no preview yet, so their scans is not held up. Set scan.requirePreview: false to turn the whole requirement off. With more than one gateway replica, set gateway.redis as well: the record lives in the gateway cache, and without a shared Redis a preview shown by one replica is not seen by another.

Jira Data Center and Server in the preview​

The Jira preview runs one search for the project you name (GET /rest/api/2/search, summaries only, one past the cap of 10) against your Jira Data Center or Server base URL, with a personal access token sent as Authorization: Bearer <token>. Jira 8.14 or later is needed for personal access tokens. Four rules apply.

  • The connection must record its type. Align picks Cloud or Data Center from the type the connection records (deployment_type: cloud or datacenter), never from the host name. A Jira connection that has a base URL, no cloud id and no recorded type gets a 409 jira_connection_type_unknown and no request is sent. Today only the Atlassian OAuth connect writes a Jira connection, and it records a cloud id and no type, so a Data Center connection has to be stored with deployment_type: datacenter for the preview to use this path.
  • The base URL is checked before a credential is sent. It must be https, with no user name, password, query or fragment, and a public address. A private-network Jira host has to be listed by you. Set JIRA_DC_ENDPOINT_ALLOWLIST on the gateway (host names, IPs or CIDRs, separated by commas, through gateway.extraEnv). The listing rules are the same as for CUSTOM_LLM_ENDPOINT_ALLOWLIST above: a listed name may resolve into private space, never to loopback, link-local or the cloud metadata addresses. Plain http needs JIRA_DC_ENDPOINT_ALLOW_HTTP=true. A refused base URL is a 409 jira_base_url_not_allowed.
  • No redirect is followed. A 3xx answer from Jira is an error, so the token cannot be sent to another host. The gateway checks the addresses a name resolves to when it connects, not only when it reads the URL.
  • Ports are not restricted. A Data Center base URL may use any port (8080 and 8443 are common). The preview sends one request to it, with the connection's own token, and returns titles only, never the raw reply.
  • The count. Jira returns a total, and the preview shows it. If your instance leaves it out, the count is shown as "at least" what one page held.
  • A private certificate authority. The gateway uses Node's certificate store. Point NODE_EXTRA_CA_CERTS at your CA file in gateway.extraEnv.

This path has not been run against a live Jira Data Center instance. Before you rely on it, run one preview against a test project and check that the token form, the count and the TLS chain work on your install.

5. What every empty default means​

Every value in the chart whose default is an empty string, grouped, so you can tell at a glance whether it is yours to fill in. "Required when" says what makes it mandatory.

Some rows say read by no template. Those are declared in values.yaml and wired to nothing in the current chart, so setting them has no effect. They are listed rather than hidden, because an operator reading 155 empty strings needs to know which ones do nothing.

Registry, images and cluster-wide settings​

ValueRequired whenIf left empty
global.imageRegistryAlwaysThe chart refuses to render.
global.imageTagAlways in practiceResolves to latest, which is not a release and is not reproducible.
global.proxy.httpProxy, global.proxy.httpsProxy, global.proxy.noProxyEgress goes through a proxyPods make direct outbound calls, which your network may drop.
global.customLlmEndpoint.allowlist (with global.customLlmEndpoint.allowHttp)A tenant points the custom LLM provider at an in-cluster model server (vLLM, Ollama)Only public https hosts are allowed, so an in-cluster address is refused when it is saved and when the Brain connects. See Allowing an in-cluster endpoint.
global.databaseSSL.provider, global.databaseSSL.customCA.secretNameNeverRead by no template. Use global.rdsSSL.enabled. No custom CA bundle is mounted by the current chart under any setting.
license.checkin.urlNeverThe gateway calls the built-in licence service address (see Licensing). Set it only if we give you a different address.
gateway.image.tag, brain.image.tag, ui.image.tag, migrations.image.tagNeverEach follows global.imageTag, which is what you want. Set one only to run a different tag for that service deliberately. The connectors are not like this: theirs default to latest rather than empty, so they do not follow the global tag. See the go-live row.

Bundled Postgres and Redis​

ValueRequired whenIf left empty
postgresql.auth.password, postgresql.auth.appPasswordNever with the Helm CLI. Always under a GitOps controller or a plain render.Generated on first install and kept. Where the chart cannot read them back, every render makes new ones while the data volume keeps the first.
postgresql.persistence.storageClassNeverThe cluster default storage class.
redis.auth.passwordNever with the Helm CLIGenerated on first install and kept.
redis.persistence.storageClassNeverThe cluster default storage class. Only read when redis.persistence.enabled is true.

Embeddings​

ValueRequired whenIf left empty
embedding.profileChat runs in your network (gpu.llmServer.enabled), or you want embeddings off public OpenAIThe default hosted profile runs and embeddings go to OpenAI. With gpu.llmServer.enabled the chart refuses to render until you set a profile or embedding.allowOpenAI=true.
embedding.requireChoiceNever: it defaults to true. Set false only if you accept embeddings going to OpenAI whenever a tenant saves Anthropic or a custom chat endpointStays true: PUT /tenant/llm-config returns 400 embedding_choice_required for non-OpenAI chat until embedding.profile is set, the tenant has a non-OpenAI embedding provider, or embedding.allowOpenAI=true is set. OpenAI chat is never refused.
embedding.azureOpenai.endpoint, embedding.azureOpenai.deploymentembedding.profile is azure-openai-te3-small-384The chart refuses to render. deployment is the name of your text-embedding-3-small deployment, not the model name.
embedding.azureOpenai.apiVersionNeverThe Brain uses its built-in default API version (2024-10-21). Set it if your Azure resource needs a different one.
embedding.compatible.baseUrl, embedding.compatible.modelembedding.profile is openai-compatible-384The chart refuses to render. The server must return 384-wide vectors.

Capture default​

ValueRequired whenIf left at the default
capture.defaultModeOn every upgrade: the chart refuses to render until you set it. On a fresh install, never. Values: "off", "propose", "auto" or "registry" (quote them)A fresh install behaves as "off": it captures nothing until a tenant admin gives a connector its own capture setting, which beats this value for that connector only. An upgrade with it unset fails the render, so a running install never changes what it captures by accident. (This guard sees helm upgrade. Argo CD, Flux and a piped helm template render as an install, so an install deployed that way gets "off" when this chart version reaches it. Set capture.defaultMode before you move to this version.) "registry" or "auto" keeps each connector's built-in default. The value only lowers, so no licence is checked.

Gateway​

ValueRequired whenIf left empty
gateway.frontendUrlui.ingress is disabled or has no host, or Align is reached at a different URLFalls back to http://align.local and every link is broken. See the go-live row.
gateway.redis.url, gateway.redis.secretNamereplicaCount above 1, or an autoscaler above 1The chart refuses to render. Session revocation would otherwise be per-pod.
gateway.queue.providerNever. Set memory to accept an in-memory queueAuto: SQS if the three gateway.sqs URLs are set, else Redis if gateway.redis is configured. With neither, the production gateway refuses to start (the in-memory queue loses scans and bulk work on restart). See the job queue.
gateway.sqs.importJobQueueUrl, gateway.sqs.bulkApprovalQueueUrl, gateway.sqs.bulkDeleteQueueUrlYou want distributed job processingJobs run on an in-process queue and queued work is lost on a pod restart.
gateway.oidc.scopesYour provider needs scopes beyond openid email profileThe code default applies.
gateway.oidc.defaultTenantIdSingle-tenant installTenant is resolved from the user's email domain instead.
selfHost.bootstrap.tenantNameselfHost.bootstrap.enabled is trueThe chart refuses to render. The Job does not guess a tenant name.
selfHost.bootstrap.tenantIdYou want to know the tenant id before the first run, to set gateway.oidc.defaultTenantId in the same releaseThe Job reuses a tenant with the same name, or makes one, and prints ALIGN_TENANT_ID=<uuid> in its log.
selfHost.bootstrap.adminEmailYou want a first org_admin before anyone signs inNo admin is created. Promote someone by hand (Step 8, part 3). An existing account is never promoted by the Job.
selfHost.bootstrap.starterRules.packsYou want no starter rules, or the healthcare pack["general"]: the four day-one rules are loaded for the first admin. Needs adminEmail and brain.enabled (the default). [] loads nothing. An unknown pack name makes the chart refuse to render.
gateway.cognito.googleIdpName, gateway.cognito.azureIdpNameYour Cognito pool's provider names differ from Google and MicrosoftThe code defaults apply.
gateway.serviceAccount.nameYou want a gateway-specific service account for cloud accessThe shared serviceAccount is used.
gateway.leads.notifyEmailsNever on a self-host: only used when gateway.leads.enabled is true, which only Align's hosted environments setNobody is emailed when a lead confirms; the lead is still stored.
gateway.leads.registeredOfficeNever on a self-host (same gate as above)The confirmation email footer uses the registered office built into the gateway.
ui.analytics.enabledNever on a self-host: only Align's hosted environments turn it onfalse: the UI pod gets no PostHog key or host, loads no analytics script and sends nothing off-host.
gateway.analytics.reviewerEmailsYou give an outside reviewer an account and want their usage kept apart from your users'Nobody is tagged reviewer. Only matters if the gateway sends product analytics, which needs a PostHog key.
gateway.analytics.internalInstallIdsYou run the CLI in local mode yourself and want those installs kept apart from your users'No local CLI install is tagged internal. Only matters if the gateway sends product analytics.

Connectors​

ValueRequired whenIf left empty
connectors.align.defaultTenantIdSingle-tenant install exposing the MCP endpointNot "no tenant". Every agent call resolves to the fixed id 00000000-0000-0000-0000-000000000001.
connectors.align.oauth.resourceUrlconnectors.align.oauth.enabled is trueThe chart refuses to render. Set it to the public MCP URL clients are given, with no trailing slash. It must match, character for character, the resource the gateway's authorization server is configured to issue tokens for.
connectors.align.ingress.classNameconnectors.align.ingress.enabled is trueThe Ingress has no class and no controller claims it.

Licence service​

Align runs this for its own customers. It is off (licence.enabled: false) and a self-host never turns it on. Its three Secret names have no default, and the chart refuses to render it without all three.

ValueRequired whenIf left empty
licence.secrets.databaselicence.enabled is trueThe chart refuses to render. The Secret holds database-url for the service's own database, never the gateway's.
licence.secrets.signingKeylicence.enabled is trueThe chart refuses to render. The Secret holds signing-private-key, the RSA key that signs the licences returned.
licence.secrets.adminTokenlicence.enabled is trueThe chart refuses to render. The Secret holds admin-token, at least 32 characters.
licence.ingress.hostlicence.ingress.enabled is trueThe chart refuses to render. No hostname is shipped; it is set with the DNS in align-infra.
licence.image.tagNeverFollows global.imageTag, which is what you want.

Secrets you supply​

With secrets.create: false, which is the production path, every row here is ignored and the value belongs in the Secret named in section 1 instead. The "Required when" column describes the development path, secrets.create: true.

ValueRequired whenIf left empty
secrets.internal.masterEncryptionKeyAlways, through values or the SecretThe gateway refuses to boot. Nothing generates this one.
secrets.internal.jwtSecret, secrets.internal.cookieSecret, secrets.internal.encryptionKey, secrets.internal.serviceAuthToken, secrets.internal.mcpAlignTokenNever, under secrets.create: trueGenerated on first install, and read back from the existing Secret on upgrade so sessions and the connector service token survive. Under create: false nothing generates them and the Secret keys must already exist.
secrets.internal.serviceAuthTokenSlack, secrets.internal.serviceAuthTokenTeams, secrets.internal.serviceAuthTokenGithubNever, under secrets.create: true. Under create: false, each is required when you run that connector (the GitHub one when you use a GitHub App).One token per relay connector. The gateway uses it to tell which connector relayed a person's conflict verdict, and hands that connector its tenant's credentials only when it presents its own token. Under create: true they are generated on first install and kept on upgrade. Without its key, the connector's credential reads get 403. See Relay connector tokens.
secrets.internal.githubInboundTokenNever, under secrets.create: true. Under create: false, add the github-inbound-token key yourself whenever connector-github is enabled.The token only the gateway holds for calls into connector-github's /internal/* routes. Both the gateway and connector-github require it (no optional), so without the key both pods stay in CreateContainerConfigError; an unset value refuses every internal call rather than falling back to serviceAuthToken. With External Secrets, add the property and set externalSecrets.githubInboundToken: true.
secrets.internal.serviceAuthTokenAlignNever, under secrets.create: true. Under create: false, add the service-auth-token-align key yourself if you run the hosted MCP server.The hosted MCP server's own token, and the only service token the gateway accepts on POST /integrations/:key/enable. Without it the MCP server falls back to serviceAuthToken: every tool works except connect, which answers 403, and the gateway warns at startup. With External Secrets, add the property and set externalSecrets.alignRelayServiceToken: true. See Hosted MCP server token.
secrets.internal.serviceAuthTokenJira, secrets.internal.serviceAuthTokenConfluence, secrets.internal.serviceAuthTokenZoom, secrets.internal.serviceAuthTokenLinear, secrets.internal.serviceAuthTokenNotion, secrets.internal.serviceAuthTokenAzureDevops, secrets.internal.serviceAuthTokenM365Documents, secrets.internal.serviceAuthTokenGoogleDriveNever, under secrets.create: true. Under create: false they are optional.One token per connector that used to call the gateway with serviceAuthToken, so the gateway can tell those connectors apart. If a key is absent that connector keeps working on serviceAuthToken, and the gateway warns at startup. With External Secrets, add all seven properties and set externalSecrets.connectorRelayServiceTokens: true. See Connector tokens.
secrets.llm.existingSecretYou want the LLM keys to stay out of your values fileEmpty: the chart renders its own <release>-llm Secret from the keys below, and the brain reads that. Set: the chart renders no LLM Secret, and the brain reads the Secret you name, with the same keys (openai-api-key, anthropic-api-key, azure-embedding-api-key, embedding-api-key). Setting it together with an inline key, or with externalSecrets.enabled, fails the render, so one Secret has one writer. See Keep the key out of your values file.
brain.preferredProviderYou hold both an OpenAI and an Anthropic key and want to choose between themEmpty: the brain uses its own default, anthropic. Accepts openai or anthropic only; anything else fails the render. Do not also set PREFERRED_PROVIDER in brain.extraEnv: that fails the render too.
secrets.llm.openaiApiKey, secrets.llm.anthropicApiKeyYou use a hosted modelThe brain has no model to call. Leave the OpenAI one empty if you run a local model, so the embeddings fallback cannot fire.
secrets.llm.azureEmbeddingApiKeyembedding.profile is azure-openai-te3-small-384The Brain refuses to start, naming AZURE_OPENAI_EMBEDDING_API_KEY. Unused on any other profile. See Embeddings.
secrets.llm.embeddingApiKeyembedding.profile is openai-compatible-384 and your server needs a bearerOptional. The Brain sends a placeholder key, which most in-cluster servers ignore.
secrets.tls.secretName, secrets.tls.commonNameNeverUnder secrets.create: true and secrets.tls.create: true they default to align-local-tls and *.align.local, and a self-signed certificate IS generated. That is a local-development path.
secrets.stripe.secretKey, secrets.stripe.webhookSecret, secrets.stripe.priceIdTeamNever in a self-hosted installNothing. These are Align's hosted billing and have no effect here.

Connector OAuth credentials​

Required for the connector you enabled, and for nothing else. An enabled connector with no credentials starts and fails when a user tries to connect it, not at install, so an empty value here does not show up until someone clicks Connect. See OAuth Apps for how to create each app.

ConnectorValues
Slacksecrets.oauth.slack.clientId, secrets.oauth.slack.clientSecret, secrets.oauth.slack.signingSecret
Slack, per-user sign-insecrets.oauth.slackPersonal.clientId, secrets.oauth.slackPersonal.clientSecret
GitHub, OAuth appsecrets.oauth.github.clientId, secrets.oauth.github.clientSecret
GitHub, Appsecrets.oauth.github.appId, secrets.oauth.github.appPrivateKey, secrets.oauth.github.appClientId, secrets.oauth.github.appClientSecret
GitHub, per-user sign-insecrets.oauth.github.personalClientId, secrets.oauth.github.personalClientSecret
Jira and Confluencesecrets.oauth.atlassian.clientId, secrets.oauth.atlassian.clientSecret (one Atlassian app covers both)
Linearsecrets.oauth.linear.clientId, secrets.oauth.linear.clientSecret
GitLabsecrets.oauth.gitlab.clientId, secrets.oauth.gitlab.clientSecret
Notionsecrets.oauth.notion.clientId, secrets.oauth.notion.clientSecret
Teamssecrets.oauth.teams.clientId, secrets.oauth.teams.clientSecret, secrets.oauth.teams.botAppId, secrets.oauth.teams.botAppPassword, secrets.oauth.teams.botAppTenantId
Azure DevOpssecrets.oauth.azureDevOps.clientId, secrets.oauth.azureDevOps.clientSecret, secrets.oauth.azureDevOps.tenantId (Entra ID app registration; no common fallback - an ADO organization is pinned to one tenant)
M365 Documentssecrets.oauth.m365Documents.clientId, secrets.oauth.m365Documents.clientSecret, secrets.oauth.m365Documents.tenantId (Entra ID app registration; SharePoint site pages are tenant-scoped, same reasoning as Azure DevOps above)
Google Drive (off by default, not usable yet)secrets.oauth.googleDrive.clientId, secrets.oauth.googleDrive.clientSecret, secrets.oauth.googleDrive.webhookSecret (a Google OAuth client separate from the sign-in one; read-only drive.readonly scope; webhookSecret signs the per-tenant Drive channel tokens, and left empty every Drive webhook is rejected). Only read when connectors.googleDrive.enabled is true, which is false by default and does nothing useful until the gateway side exists (ALI-1575)
Google Meet (off by default, not usable yet)secrets.oauth.googleMeet.clientId, secrets.oauth.googleMeet.clientSecret, secrets.oauth.googleMeet.webhookSecret (a Google OAuth client separate from the sign-in one; read-only meetings.space.readonly scope; webhookSecret signs the per-tenant Pub/Sub push token, and left empty every Meet webhook is rejected). Only read when connectors.googleMeet.enabled is true, which is false by default and does nothing useful until the gateway side exists (ALI-1658)
Google Meet events (off by default, not usable yet)connectors.googleMeet.eventsTopic, connectors.googleMeet.eventsTenantId (one Pub/Sub topic for one tenant, the dogfood setup; eventsTopic is projects/<project>/topics/<topic> and eventsTenantId is the tenant UUID allowed to use it. Set both or neither: the chart refuses to render a topic without a tenant. Left empty, no Workspace Events subscription is created and the hourly renewal sweep does not start, so Meet never delivers events). Only read when connectors.googleMeet.enabled is true. Transcript polling needs neither of these: while connectors.googleMeet.enabled is true the gateway polls every 5 minutes for each tenant with a connected Meet account, from the moment they connect and never 30 days back. It has no chart value; set GOOGLE_MEET_TRANSCRIPT_POLL=disabled, GOOGLE_MEET_TRANSCRIPT_POLL_INTERVAL_MS (default 300000, minimum 60000) or GOOGLE_MEET_POLL_BACKFILL_HOURS (first poll only, up to 720) in gateway.extraEnv
Shared Entra app (Teams, Azure DevOps, SharePoint)secrets.oauth.entra.clientId, secrets.oauth.entra.clientSecret, secrets.oauth.entra.tenantId (one registration for all three flows; a per-flow block above wins; ALI-1455)

This table is not the whole credential surface, and the gap is worth knowing rather than glossing. Several webhook and App credentials are read by the chart from Secret keys that values.yaml declares no value for, so they can only be supplied by pre-creating the Secret: the GitHub App's webhook-secret and app-installation-id (in align-oauth-github), Zoom's client-id, client-secret and webhook-secret (in align-oauth-zoom), and the Teams notification client-state (in align-teams-webhook). A webhook secret is a signature check, so treat an unset one as a control you have not configured.

Teams, delegated-only install​

connectors.teams.disableRealtimeTranscripts (default false). Set true if your Azure AD admin granted only delegated permissions and not an Application permission for Teams meeting transcripts - a pilot tenant is the usual case. The connector otherwise calls Microsoft Graph with an app-only token at startup, for every mapped tenant, to keep a tenant-wide real-time transcript subscription alive; without the Application permission that call is rejected and logged as a failure on every start. With the switch on, the connector skips that call entirely - no app-only token, no subscription, no startup failure - and still serves Discover scans over your delegated OAuth scopes.

Cloud secret manager​

ValueStatus
externalSecrets.secretStore.nameRequired when externalSecrets.enabled is true.
externalSecrets.provider, externalSecrets.aws.region, externalSecrets.azure.vaultUrl, externalSecrets.azure.tenantId, externalSecrets.gcp.projectId, externalSecrets.vault.serverRead by no template. Provider selection comes from the SecretStore you point at, and externalSecrets.aws.secretPath is used as the remote key prefix for every provider.

Telemetry export, tracing and GPU​

ValueRequired whenIf left empty
telemetry.export.prometheus.pushGatewayUrl, telemetry.export.datadog.apiKeySecretNameNeverRead by no template. There is no telemetry export in the current chart; usage metrics stay in your database. For metrics scraping use podMonitor.enabled, and for traces use observability below.
observability.grafanaCloud.otlpEndpointobservability.grafanaCloud.enabled is trueThe collector has no endpoint to export to.
observability.grafanaCloud.authToken, observability.datadog.apiKeyNeverRead by no template. The collector reads the credential from a Secret named align-otel, and that reference is required, so enabling a backend without the Secret gives you a collector pod that will not start.
gpu.llmServer.quantization, gpu.llmServer.maxModelLenThe model you chose needs themvLLM's own defaults apply, which for a large model on a small GPU means it fails to load.

Keeping this page true​

A hand-written page describing 153 settings goes stale the first time someone adds one. scripts/check-values-reference-parity.sh runs in the repository's Repo Guards job and fails when a top-level key, or a value whose default is an empty string, appears in charts/align/values.yaml and nowhere on this page. It also compares the two counts stated at the top of this page against the chart. Deliberate omissions go in scripts/config/values-reference-allowlist.txt with a date and a reason, and an entry that no longer matches anything fails as drift, so the list cannot accumulate stale exemptions.

What that guard checks, stated so a green run is not read as more than it is: that every such key is mentioned here, and that the two counts are right. It cannot check that what this page says about a key is true, and it cannot see a setting the chart reads but values.yaml never declares. Those parts are a read.