Install with kindo-cli
Install the CLI, generate your configuration, and check your infrastructure before deploying Self-Managed Kindo (SMK) to your prepared Kubernetes cluster.
1. Install the CLI
Section titled “1. Install the CLI”The Kindo CLI is distributed as an OCI artifact at oci://registry.kindo.ai/kindo-cli/kindo-cli, pulled with helm using the same registry.kindo.ai credentials as the rest of the install. It contains the CLI wheel (kindo_cli-*.whl) and a SHA256SUMS file.
Prerequisites on your install host
Section titled “Prerequisites on your install host”| Tool | Required | Version | Purpose |
|---|---|---|---|
kubectl | Yes | 1.32+ | Cluster access |
helm | Yes | v4+ | Chart deployment |
helmfile | Yes | v1.2+ | Release orchestration |
yq | Yes | v4+ | YAML processing |
jq | Yes | any | Install scripts |
| Python | Yes | 3.11+ | CLI runtime (installable via uv) |
uv | Recommended | latest | Python + package manager in one binary |
psql | Optional | any | kindo db tunnels and prompts |
| Registry creds | Yes | n/a | registry.kindo.ai username + password (provided by Kindo) |
Step 1: Install Python 3.11+ (skip if you already have it)
Section titled “Step 1: Install Python 3.11+ (skip if you already have it)”Check your current Python version:
python3 --versionIf it reports 3.11 or higher, skip to Step 2. Otherwise install uv, which will also install Python 3.11 for you.
curl -LsSf https://astral.sh/uv/install.sh | shexport PATH="$HOME/.local/bin:$PATH"uv --versionStep 2: Pull the CLI artifact
Section titled “Step 2: Pull the CLI artifact”Log in to the Kindo registry, then pull and unpack the artifact at your target release version:
helm registry login registry.kindo.ai --username <registry-username>helm pull oci://registry.kindo.ai/kindo-cli/kindo-cli --version <release> --untarVerify the wheel checksums before installing:
(cd kindo-cli && sha256sum -c SHA256SUMS)For air-gapped installs, pull the .tgz on a connected machine and transfer it to the install host, or pull from a registry that mirrors registry.kindo.ai.
Step 3: Install the CLI wheel
Section titled “Step 3: Install the CLI wheel”Recommended: via uv (isolates the CLI in its own environment):
uv tool install --force ./kindo-cli/kindo_cli-*.whlAlternative: via pip (if you already have Python 3.11+):
python3.11 -m pip install ./kindo-cli/kindo_cli-*.whlThe glob matches the version in the wheel’s filename.
To upgrade, pull the target release and re-run the same install command.
Step 4: Verify the installation
Section titled “Step 4: Verify the installation”kindo --versionkindo --helpIf the help output prints, the CLI is installed and on your PATH. If kindo is not found, ensure $HOME/.local/bin is on PATH (uv installs CLIs there by default).
2. Generate config with kindo config init
Section titled “2. Generate config with kindo config init”The config wizard produces the two files every subsequent command reads:
install-contract.yaml: what to deploy (version, base domain, registry credentials, superadmin email (operatorEmail), applications, peripheries, secrets-store backend, SMTP, optional integrations).environment-bindings.yaml: where your infrastructure lives (PostgreSQL admin connection strings, an optional dedicated Hatchet instance, Redis, RabbitMQ, S3-compatible storage).
Run the wizard in the directory where you want the config files written:
kindo config initWizard sections
Section titled “Wizard sections”Draft state is saved to .kindo-config-draft.json as you go, so you can quit with Ctrl-C and resume later.
-
Deployment Basics: base domain, application version, storage class.
-
Container Registry:
registry.kindo.aiusername and password (provided by Kindo). -
Operator: your email address.
kindo install --applycreates this account as the install’s first superadmin and prints its key once. -
PostgreSQL: main admin connection string, auxiliary admin connection string (the same as main is fine for a single instance), and an optional Hatchet admin connection string. A dedicated instance for Hatchet is recommended; press Enter to keep Hatchet on the auxiliary instance. The optional connection is written as
postgres.adminHatchetinenvironment-bindings.yaml. See PostgreSQL settings for Hatchet. -
Redis: connection string for the standalone Redis.
-
RabbitMQ: connection string for RabbitMQ (AMQP or AMQPS).
-
Object Storage: S3-compatible bucket name, region, optional static access/secret keys, optional custom endpoint for a store other than AWS S3. If that endpoint is only resolvable inside the cluster, also give a browser-reachable public endpoint for the same storage so download links work for end users.
-
SMTP: an authenticated SMTP relay is required. Provide the host, user, password, and From address; port defaults to 587.
-
Secrets Store:
kubernetes(Secrets stored in the cluster, the default),azurekv(Azure Key Vault),vault(HashiCorp Vault), oraws(AWS Secrets Manager). External backends prompt only for that backend’s connection and auth fields. If you choose an external backend, complete External Secrets Store first. -
Integrations (optional): toggle MCP integrations here, or add them later with
kindo integrations enable <name>.
Draft-resume behavior
Section titled “Draft-resume behavior”If you quit partway through, re-running kindo config init detects the draft and offers to continue where you left off:
Found incomplete wizard draft (completed through section 4/10) Continue where you left off? [Y/n]:If the final install-contract.yaml and environment-bindings.yaml files already exist, the wizard offers to load them as defaults and edit in place.
Output
Section titled “Output”On success the wizard prints:
✓ Configuration files created
install-contract.yaml environment-bindings.yamlFull example install-contract.yaml
apiVersion: kindo.ai/v1metadata: schemaVersion: '2'baseDomain: kindo.example.comappVersion: <release>storageClass: <storage-class>registry: username: <registry-username> password: <registry-password>applications: api: true next: true superadmin: true litellm: true credits: true cerbos: true prisma-migrations: true hatchet: true nango: true task-worker-ts: true mcp-unified: true mcp-platform: true sandbox: trueperipheries: qdrant: true presidio: true speaches: true otel-collector: trueoperatorEmail: operator@example.comsecretsStore: backend: kubernetessmtp: host: smtp.example.com port: '587' user: <smtp-user> password: <smtp-password> fromEmail: noreply@kindo.example.comlogLevel: infoingress: enabled: true className: nginx annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: 'true' nginx.ingress.kubernetes.io/proxy-body-size: '0' tls: mode: cert-manager clusterIssuer: letsencrypt-prodThe ingress: block is optional. With it omitted, route the hosts from kindo ingress manifest through your own controller. With enabled: true, the CLI renders one Ingress per host through className, applies annotations to every Ingress, and handles TLS per tls.mode: cert-manager, secret, acm (AWS Certificate Manager), or none. Hosts follow endpoints and baseDomain.
The example uses ingress-nginx with cert-manager. Each host gets cert-manager.io/cluster-issuer: letsencrypt-prod and a spec.tls entry with secret <release>-<instance>-tls (for example api-main-tls). For streaming chat responses on nginx, set the api release’s buffering and timeouts:
kindo config helm-override set api \ 'ingress.annotations.nginx\.ingress\.kubernetes\.io/proxy-buffering=off' \ 'ingress.annotations.nginx\.ingress\.kubernetes\.io/proxy-read-timeout=3600' \ 'ingress.annotations.nginx\.ingress\.kubernetes\.io/proxy-send-timeout=3600'For other ingress controllers, use the following examples:
AWS Load Balancer Controller with an ACM certificate
ingress: enabled: true className: alb annotations: alb.ingress.kubernetes.io/group.name: kindo-shared alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]' alb.ingress.kubernetes.io/ssl-redirect: '443' tls: mode: acm acmCertificateArn: arn:aws:acm:us-west-2:123456789012:certificate/<certificate-id>On alb, set load-balancer-level keys (scheme, certificate, listeners, security groups) only in the contract’s ingress.annotations; a per-release override of them is rejected at install and upgrade.
Kong Ingress Controller with a pre-provisioned wildcard certificate
ingress: enabled: true className: kong annotations: konghq.com/protocols: https konghq.com/https-redirect-status-code: '301' konghq.com/strip-path: 'false' tls: mode: secret secretName: kindo-wildcard-tlsCreate kindo-wildcard-tls in each publishing namespace. Each host gets a spec.tls entry referencing that Secret. For SSE on api, raise Kong’s route timeouts (milliseconds):
kindo config helm-override set api \ 'ingress.annotations.konghq\.com/read-timeout=3600000' \ 'ingress.annotations.konghq\.com/write-timeout=3600000'mcp-platform (the agent tools adapter) is installed by default, and new installs turn on Platform tools and MCP account routing through the PLATFORM_MCP and MCP_ACCOUNT_ROUTING feature flags. MCP account routing lets users select more than one integration connection for a tool server. To opt out of the adapter, set applications.mcp-platform: false and turn off PLATFORM_MCP in Unleash at https://unleash.<domain>.
The superadmin dashboard is installed by default and served at superadmin.<domain>. Publish its DNS record with the others (DNS subdomains).
Full example environment-bindings.yaml
apiVersion: kindo.ai/v1postgres: admin: connectionString: postgresql://<admin-user>:<admin-password>@postgres.example.com:5432/postgres?sslmode=require adminAuxiliary: connectionString: postgresql://<admin-user>:<admin-password>@postgres-auxiliary.example.com:5432/postgres?sslmode=require adminHatchet: connectionString: postgresql://<admin-user>:<admin-password>@postgres-hatchet.example.com:5432/postgres?sslmode=requireredis: connectionString: redis://redis.example.com:6379rabbitmq: connectionString: amqps://<rabbitmq-user>:<rabbitmq-password>@rabbitmq.example.comstorage: bucketName: kindo-uploads region: <region> accessKey: <storage-access-key> secretKey: <storage-secret-key> endpointUrl: https://storage.example.com endpointUrlPublic: https://downloads.example.com3. Preflight your infrastructure
Section titled “3. Preflight your infrastructure”Check that your cluster can reach the dependencies listed in environment-bindings.yaml:
kindo config validate --preflightkindo config validate checks the configuration schema. With --preflight, it also:
-
Creates the
kindo-systemnamespace if missing. -
Uploads your
environment-bindings.yamlas a Kubernetes Secret (kindo-preflight-config). -
Creates the image-pull secret for
registry.kindo.aiand performs ahelm registry login. -
Deploys the
preflightchart intokindo-system. -
Waits up to about five minutes for the
kindo-preflightJob to finish and shows its logs.
The checks run inside your cluster, in this order:
- PostgreSQL: main and auxiliary admin endpoints reachable, credentials valid.
- Hatchet database: the Hatchet instance and its recommended settings; see PostgreSQL settings for Hatchet.
- Redis: reachable on the provided connection string.
- RabbitMQ: reachable (AMQP or AMQPS).
- S3-compatible storage: bucket accessible with the provided credentials or workload identity. Warns when the storage endpoint resolves only inside the cluster and no public endpoint is set, because user download links would then be unreachable; see S3-compatible object storage.
- SMTP: handshake and authentication.
- DNS:
baseDomainand its derived subdomains resolve. - Nodes: enough ready nodes.
- StorageClass: the configured
storageClassresolves in the cluster. - cert-manager: present when TLS is expected.
- Ingress controller: at least one IngressClass is available.
4. Preview with kindo install --plan
Section titled “4. Preview with kindo install --plan”--plan provides a read-only preview of the install:
kindo install --planIt prints:
- Install settings: domain, version, Postgres host (password masked), storage bucket.
- Prerequisites: a green tick per required tool and a dot per optional tool.
- Cluster reachability: confirms your kubeconfig can talk to the cluster.
- Steps: the ordered list of install steps with their one-line descriptions.
The plan also flags a missing operatorEmail.
5. Apply with kindo install --apply
Section titled “5. Apply with kindo install --apply”Once the plan looks right, run the install:
kindo install --apply--apply stops before changing anything if operatorEmail is missing from install-contract.yaml.
The installer runs these steps in order:
| # | Step | Purpose |
|---|---|---|
| 1 | generate-secrets | Generate secrets and store them in the cluster. |
| 2 | merge-bindings | Merge infrastructure bindings into the secrets config. |
| 3 | setup-registry | Configure image pull secrets in all namespaces. |
| 4 | external-secrets | Install the External Secrets Operator (operator, CRDs, webhook). The installer uses an existing ESO if it serves external-secrets.io/v1 (ESO 0.17 or later). An older ESO fails the step with upgrade guidance. |
| 5 | db-bootstrap | Create databases and service users. |
| 6 | peripheries | Deploy Unleash (+ Edge), Qdrant, Presidio, Speaches, Hatchet, the OTel Collector, ClickHouse and HyperDX. See Observability. |
| 7 | hatchet-token | Store the Hatchet API token in the secrets config. |
| 8 | migrations | Run database schema migrations. |
| 9 | applications | Deploy the application services enabled in install-contract.yaml. The wizard enables api, next, superadmin (the superadmin dashboard), litellm, credits, cerbos, prisma-migrations, hatchet, nango, task-worker-ts, mcp-unified, mcp-platform, and sandbox. |
| 10 | superadmin-bootstrap | Create the superadmin account for operatorEmail and print its key once. Re-runs print already bootstrapped and issue no key. The step fails if no superadmin exists afterwards. |
| 11 | post-install | Nango bootstrap and feature flags. |
| 12 | integrations | Reconcile enabled integrations. Skips when none are enabled. A failure is recorded and reported, and the install finishes with a warning that integrations failed. Re-run with kindo install --step integrations. |
During execution the CLI streams a live table of Helm release states (◐ pending, ✓ deployed, ✗ failed) plus the notable lines from scripts ([STEP], [INFO], [WARN]). Add --verbose to see the raw helmfile output.
6. Resume or replay steps
Section titled “6. Resume or replay steps”After a failure (or an intentional Ctrl-C), resume with:
kindo install --resume--resume stops before changing anything if operatorEmail is missing. It skips steps marked completed in kindo-install-state and picks up at the first unfinished step. Existing generated secrets are preserved.
If the contract has changed since the last run, the CLI warns you:
Warning: Config changed since last run. Verify changes are intentional.The warning means you changed the config files between runs. Check that the change was intended. The install continues.
Re-run a single step by name:
kindo install --step applicationskindo install --step post-installkindo install --step hatchet-token--step runs the named step independently and updates its state in kindo-install-state on completion. Use it to retry a failed step.
Use --release <name> to narrow a group step to one release:
kindo install --step applications --release apikindo install --step superadmin-bootstrap re-runs the bootstrap. If a superadmin already exists, it issues no key. It exits non-zero if no superadmin exists afterwards.
kindo install --step sync-flags adds feature flags new in the target version without changing existing ones.
7. State introspection
Section titled “7. State introspection”Install state lives in the cluster. Anyone with cluster access and the CLI can observe or resume an install.
kindo status
Section titled “kindo status”kindo statusPrints Version, Started, Contract hash, and a per-step table:
Kindo Install State Version: 2026.09.0 Started: 2026-09-25T14:02:17 Contract hash: 7c0a3e...
Step Status Completed Errorgenerate-secrets ✓ completed 2026-09-25T14:02:35merge-bindings ✓ completed 2026-09-25T14:02:41setup-registry ✓ completed 2026-09-25T14:02:48external-secrets ✓ completed 2026-09-25T14:04:12db-bootstrap ✓ completed 2026-09-25T14:05:02peripheries → runninghatchet-token ○ pendingmigrations ○ pendingapplications ○ pendingsuperadmin-bootstrap ○ pendingpost-install ○ pendingintegrations ○ pendingBelow the step table, the Superadmin: line reports whether the install has a superadmin, for example ✓ bootstrapped. A live release table follows with each release’s chart version, image tags, and health. Use kindo status --no-live to skip the live checks. See Operate for interpreting status and diagnosing failures.
kindo-install-state ConfigMap
Section titled “kindo-install-state ConfigMap”State is stored in the kindo-install-state ConfigMap in the kindo-system namespace:
kubectl -n kindo-system get cm kindo-install-state -o yamlKeys:
meta.json:{ version, contract_hash, started_at }step.<name>:{ status, started_at, completed_at, error }for each step, wherestatus ∈ { pending, running, completed, failed, skipped }.
The ConfigMap is created on first run and labeled app.kubernetes.io/managed-by=kindo-cli. The CLI manages lifecycle; do not edit it by hand except in break-glass scenarios.
kindo-secrets-config Secret
Section titled “kindo-secrets-config Secret”All cluster-resident secrets (generated passwords, API keys, encryption keys, merged infrastructure connection strings, Hatchet client token) live in the kindo-secrets-config Secret in kindo-system:
kubectl -n kindo-system get secret kindo-secrets-config -o jsonpath='{.data.secrets\.yaml}' | base64 -dRestrict who can read this Secret.
Managing integrations
Section titled “Managing integrations”To enable, disable, and deploy integrations after the install, see kindo integrations.
