Configure & Validate
By the time you arrive here, kindo install --apply has finished and every pod in the stack should be Running. This page covers the final mile: publishing DNS, terminating TLS at the ingress, wiring up SSO, seeding the first org and admin user, and running a smoke test that proves the install is truly usable.
If the CLI’s post-install step (the final step of kindo install --apply) completed cleanly, most of the SSO and admin-user work below is already done — this page shows you how to verify it and tells you what to do when you need to adjust.
What ships out of kindo install
The post-install step performs these bootstrap actions automatically:
- Runs SSOReady database migrations.
- Creates the first Enterprise org and admin user in the Kindo database from
adminUser.emailandadminUser.nameininstall-contract.yaml. - Runs the SSO bootstrap using
adminUser.ssoDomain— seeds SSOReady infrastructure rows, creates or updates the SSOReady organization, links it to the Kindo org, and prints a one-time self-serve setup URL. - Registers any models defined in
models[]and imports feature flags to Unleash.
What’s still left to do:
- Publishing DNS
- Terminating TLS
- Running the SAML connection through the setup URL
- Smoke-testing the result
1. Publish DNS records
-
List the ingress endpoints your ingress controller has provisioned:
Terminal window kubectl get ingress -AOn AWS with the ALB controller, expect one ALB hostname shared across all Kindo services. On NGINX / Traefik / cloud LB controllers, you’ll see a single external IP or hostname attached to each ingress. Example output:
NAMESPACE NAME CLASS HOSTS ADDRESSapi api-ingress alb api.kindo.example.com k8s-kindoshared-abc.us-west-2.elb.amazonaws.comnext next-ingress alb app.kindo.example.com k8s-kindoshared-abc.us-west-2.elb.amazonaws.comssoready ssoready-ingress alb sso.kindo.example.com ... k8s-kindoshared-abc.us-west-2.elb.amazonaws.com -
Create DNS records for every hostname under your base domain. Either publish a wildcard or explicit records — both work.
Wildcard (simplest):
*.kindo.example.com CNAME k8s-kindoshared-abc.us-west-2.elb.amazonaws.comExplicit (when you need per-host firewall rules, specific target ports, or custom health checks):
Host Backend service Port Health check Access app.kindo.example.comnext80 /api/_healthWhitelisted (users) api.kindo.example.comapi80 /healthcheckWhitelisted (users + frontend) integrations-api.kindo.example.comnango80 /healthPublic — integration OAuth callbacks integrations-connect.kindo.example.comnango-connect-ui3009 /Whitelisted sso.kindo.example.comssoready-admin80 /healthWhitelisted sso-auth.kindo.example.comssoready-auth80 /healthWhitelisted (SAML auth flow) sso-api.kindo.example.comssoready-api80 /healthWhitelisted sso-app.kindo.example.comssoready-app80 /healthWhitelisted hatchet.kindo.example.com(/api/*)hatchet-api8080 /api/readyWhitelisted (task worker traffic) hatchet.kindo.example.com(/*)hatchet-frontend8080 /api/readyWhitelisted (admin UI) litellm.kindo.example.comlitellm4000 /health/livelinessWhitelisted unleash.kindo.example.comunleash4242 /healthWhitelisted unleash-edge.kindo.example.comunleash-edge3063 /healthWhitelisted -
Wait for propagation, then confirm from outside the cluster:
Terminal window dig +short app.kindo.example.comdig +short api.kindo.example.com
Routing requirements that can’t be left at defaults
A few ingress rules ship with non-obvious constraints. If your ingress controller or WAF is applying default behavior, Kindo features will break in ways that are hard to diagnose later:
- Server-Sent Events on
/express/chat— the API streams chat responses as SSE. Your ingress must disable response buffering, allow read/send timeouts ≥ 300s, impose no response body size limit, and apply no content transformation on responses. In NGINX that’sproxy_buffering offplus generousproxy_read_timeout/proxy_send_timeout. In Traefik setrespondingTimeouts; in Envoy setstream_idle_timeout. Default buffering + 60s timeouts will silently truncate every chat response. - Hatchet path-based routing on
hatchet.<domain>— the host fronts two backends./api/*must route tohatchet-apiwith higher priority than/*which routes to the admin UI (hatchet-frontend). Wrong priority sends task-worker traffic to the UI and nothing works. - Non-VPC CNIs (Calico, Cilium) — the AWS ALB Controller’s default
target-type=ipcannot reach pod IPs on non-VPC CNIs. Either switch affected services toNodePortand usetarget-type=instance, or run an in-cluster ingress (NGINX, Traefik, Istio) instead of the ALB controller.
2. Terminate TLS at the ingress
You have two paths, pick one.
Option A — cert-manager (recommended)
If your cluster has cert-manager installed and a ClusterIssuer (Let’s Encrypt, private CA, Vault, etc.), annotate the Kindo ingresses to request certificates automatically:
kubectl annotate ingress -A \ -l app.kubernetes.io/part-of=kindo \ cert-manager.io/cluster-issuer=letsencrypt-prod --overwriteOr create a single Certificate resource that covers every Kindo host and reference its secret from each ingress’s tls: block.
Verify cert issuance:
kubectl get certificate -Akubectl describe certificate <name> -n <namespace>Option B — manual certs
Create Kubernetes TLS secrets from certs issued by your private CA or certificate vendor, then reference them from each ingress:
kubectl create secret tls kindo-tls \ -n <namespace> \ --cert=tls.crt \ --key=tls.keyThe secret name must match what the ingress tls.secretName references. If you use one wildcard cert for all hosts, create the secret in each ingress’s namespace (or use a tool like reflector to replicate it).
3. Verify the first org and admin user
The post-install step reads these fields from install-contract.yaml:
baseDomain: kindo.example.com
adminUser: name: Admin User # display name (default: "Admin User") ssoDomain: example.com # if set, also triggers SSO bootstrap for this domain
bootstrapAdmin: enabled: true # enables password-based break-glass admin login password: <strong-password> # required when enabled=trueBoth adminUser and bootstrapAdmin are defined in tools/kindo-cli/src/kindo_cli/config/contract.py. adminUser drives the SSO-backed Kindo admin; bootstrapAdmin is a password-backed break-glass account for first login and recovery — keep its password in your secret vault.
Verify the admin user exists by querying the main database. The simplest route is via the CLI’s DB tunnel:
kindo db tunnel main --print# copy the DATABASE_URL it prints, then:psql "$DATABASE_URL" -c 'SELECT id, email, "orgRole" FROM "User";'You should see one row with the email you set in adminUser.email and orgRole = 'Admin'.
4. Finish SSO via SSOReady
The post-install step has already:
- Seeded SSOReady’s
app_organizations,environments,api_keys, andsaml_oauth_clientstables. - Created an SSOReady organization keyed on
adminUser.ssoDomain(external ID + domain). - Linked it to the Kindo org by writing
ssoReadyOrgIdonto theOrgrow. - Generated a one-time self-serve setup URL and printed it to stdout.
The SSOReady “Admin” role: SSOReady ships four services — ssoready-auth, ssoready-api, ssoready-app, and ssoready-admin. The ssoready-admin UI (https://sso.<baseDomain>) is for SSOReady administrators — people who manage SAML connections and organizations inside SSOReady itself. It is distinct from the Kindo admin role. post-install provisions an admin session token for this UI and prints a console-snippet to sign in; keep that snippet with your break-glass credentials.
-
Recover the setup URL if you lost the original output. Re-run the
post-installstep — it is idempotent and will reprint the self-serve setup URL:Terminal window kindo install --step post-install -
Hand the setup URL to your IdP admin. They complete the SAML connection in Okta / Entra / Google / JumpCloud / ADFS / Ping. The canonical attribute mapping is
email,firstName,lastName; see SSO Setup for provider-specific guides. -
Verify the connection back in the SSOReady admin UI (
https://sso.<baseDomain>). The SAML connection should appear and JIT provisioning is enabled automatically. -
Adjust
adminUser/bootstrapAdminlater by editing the contract withkindo config editand re-running the post-install step:Terminal window kindo config editkindo install --step post-installThe
post-installstep is idempotent — it skips existing users and orgs, and the SSO bootstrap within it patches the SSOReady organization if the display name or domain changed.
5. Smoke test
Run every one of these. The stack is only “configured” once they all pass.
5.1 Install state from the CLI
kindo statusEvery step from generate-secrets through post-install should show completed. If anything is failed, resolve it before continuing with the rest of this page — kindo install --resume picks up from the failed step.
5.2 Pod health rollup
for ns in api next litellm llama-indexer ssoready cerbos task-worker-ts \ external-poller external-sync credits audit-log-exporter \ unleash unleash-edge qdrant presidio speaches hatchet nango; do echo "=== $ns ===" kubectl get pods -n "$ns" 2>/dev/nulldoneEvery pod should be Running with the expected READY count (e.g. 1/1, 2/2). Jobs (migrations, bootstrap) are expected to be in Completed.
5.3 API health check
curl -fsS https://api.kindo.example.com/healthcheckExpected: HTTP 200. If TLS fails, rerun the cert-manager verification from step 2. If you get a 502 / 504, check the API pod logs (kubectl logs -n api -l app.kubernetes.io/name=api).
5.4 Manual chat test via the UI
- Open
https://app.kindo.example.comin a private window. - Click Sign in with SSO, enter the SSO domain you configured, and complete the IdP login.
- Send a short message to the default model.
- Confirm the response streams back. If it doesn’t stream at all (response arrives as a single chunk), your ingress is buffering — revisit the streaming requirements in AWS Applications — Webhooks and Streaming.
If SSO is not yet live, you can log in with the bootstrapAdmin password instead by pointing the browser at https://app.kindo.example.com/login and using the email from adminUser.email plus the bootstrapAdmin.password value.