Install with kindo-cli
kindo-cli is the supported installer for Self-Managed Kindo (SMK). This page walks through installing the CLI binary, generating the two config files, running the preflight check against your infrastructure, and applying the install steps against your prepared Kubernetes cluster.
1. Install the CLI
The Kindo CLI ships as a Python wheel delivered via a time-limited, signed S3 URL. Kindo Support provides the exact URL at install kickoff — substitute it for <cli-wheel-url> in the commands below.
Prerequisites on your install host
| Tool | Required | Version | Purpose |
|---|---|---|---|
kubectl | Yes | matches cluster | Cluster access |
helm | Yes | v3.12+ | Chart deployment |
helmfile | Yes | v0.159+ | Release orchestration |
yq | Yes | v4+ | YAML processing |
| 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 | — | registry.kindo.ai username + password (provided by Kindo) |
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, a single static binary that works on RHEL 8/9, Amazon Linux, Ubuntu, and most distros without root or additional system packages. uv 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: Install the Kindo CLI
Recommended — via uv (isolates the CLI in its own environment):
uv tool install --python 3.11 "<cli-wheel-url>"Alternative — via pip (if you already have Python 3.11+):
python3.11 -m pip install "<cli-wheel-url>"Always keep the URL in double quotes — the S3 signed URL contains & and = characters that the shell would otherwise misinterpret.
Step 3: Verify the installation
kindo --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
The config wizard produces the two files every subsequent command reads:
install-contract.yaml— what to deploy (version, base domain, registry credentials, admin user, applications, peripheries, optional SMTP/models/integrations).environment-bindings.yaml— where your infrastructure lives (Postgres admin connection strings, Redis, RabbitMQ, S3-compatible storage).
Run the wizard in the directory where you want the config files written:
kindo config initThe ten wizard sections
The wizard walks through ten sections. Draft state is saved to .kindo-config-draft.json after each section, 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). -
Initial Admin User — admin email, display name, SSO domain, and whether to enable the bootstrap username/password login for first access.
-
PostgreSQL — main admin connection string and an auxiliary admin connection string (same instance is fine for small deployments).
-
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 MinIO or GCS.
-
SMTP (optional) — host, port, credentials, and From address if you want Kindo to send email.
-
AI Models — at minimum an embedding model, a transcription model, and one chat LLM. Providers include AWS Bedrock, Anthropic, OpenAI, or a custom LiteLLM-compatible endpoint. See Prepare AI Models for how to pick and stage these.
-
Integrations (optional) — toggle MCP integrations now or add them later with
kindo integrations enable <name>.
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/9) 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 instead of starting fresh.
Output
On success you get the two files plus a configuration summary table:
✓ Configuration files created
install-contract.yaml — review applications & peripheries environment-bindings.yaml — verify connection stringsFull example install-contract.yaml
apiVersion: kindo.ai/v1metadata: schemaVersion: '1'baseDomain: marcos.kin.doregistry: username: robot$aws_demo password: REDACTEDappVersion: 2026.03.15storageClass: gp2applications: api: true next: true litellm: true credits: true external-sync: true external-poller: true audit-log-exporter: true cerbos: true ssoready: true prisma-migrations: true hatchet: true nango: true task-worker-ts: true mcp-unified: false sandbox: falseperipheries: unleash: true qdrant: true presidio: true speaches: truebootstrapAdmin: enabled: true password: CHANGE_MEadminUser: name: Admin User ssoDomain: 'kindo.ai'Full example environment-bindings.yaml
apiVersion: kindo.ai/v1postgres: admin: connectionString: postgresql://ADMIN_USER:ADMIN_PASSWORD@kindo-production-postgres-main.TEST.us-west-2.rds.amazonaws.com:5432/postgres?sslmode=no-verify adminAuxiliary: connectionString: postgresql://ADMIN_USER:ADMIN_PASSWORD@kindo-production-postgres-auxiliary.TEST.us-west-2.rds.amazonaws.com:5432/postgres?sslmode=no-verifyredis: connectionString: redis://kindo-production-redis.TEST.ng.0001.usw2.cache.amazonaws.com:6379rabbitmq:storage: bucketName: kindo-uploads region: us-west-2 accessKey: REDACTED secretKey: NANqUEdAH0REDACTED3. Preflight your infrastructure
Before running any install step, prove that your cluster can actually talk to the dependencies listed in environment-bindings.yaml.
kindo config validate --preflightPlain kindo config validate only checks schema. Adding --preflight does more:
-
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
preflightHelm chart fromoci://registry.kindo.ai/kindo-helm/preflightintokindo-system, enabling cluster-side checks. -
Waits up to two minutes for the
kindo-preflightJob to finish and streams its logs.
The Job is run inside your cluster, so it tests the exact network path your application pods will use. It verifies:
- PostgreSQL — main admin and auxiliary admin endpoints reachable, credentials valid.
- Redis — reachable on the provided connection string.
- RabbitMQ — reachable (AMQP or AMQPS).
- S3-compatible storage — bucket accessible with the provided credentials or workload identity.
- StorageClass — the configured
storageClassresolves in the cluster. - cert-manager — present when TLS is expected.
- DNS —
baseDomainand its derived subdomains resolve. - Ingress controller — at least one IngressClass is available.
4. Preview with kindo install --plan
--plan is a read-only operation that prints exactly what the installer will do without touching the cluster:
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.
5. Apply with kindo install --apply
Once the plan looks right, run the install:
kindo install --applyThe installer runs the steps below in order. Each step marks itself running, then completed or failed, in the kindo-install-state ConfigMap as it goes.
| # | Step | Purpose |
|---|---|---|
| 1 | generate-secrets | Generate passwords, API keys, and encryption keys; persist in-cluster. |
| 2 | merge-bindings | Merge environment-bindings.yaml + contract values into the secrets config. |
| 3 | setup-registry | Create registry-credentials image-pull secrets in every app namespace. |
| 4 | db-bootstrap | Create per-service Postgres databases and service users. |
| 5 | peripheries | Deploy Unleash (+ Edge), Qdrant, Presidio, Speaches, Hatchet (pre-creates hatchet secret). |
| 6 | hatchet-token | Read Hatchet’s boot-time client config and store the token in the secrets config. |
| 7 | migrations | Run Prisma migrations via the prisma-migrations release. |
| 8 | applications | Deploy the Kindo application services enabled in install-contract.yaml — typically api, next, litellm, credits, external-sync, external-poller, audit-log-exporter, cerbos, ssoready, nango, task-worker-ts (plus optional mcp-unified, sandbox). |
| 9 | post-install | Bootstrap admin user, SSO, Nango, models, and feature flags from the contract. |
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
After a failure (or an intentional Ctrl-C), resume with:
kindo install --resume--resume reads kindo-install-state, skips any step marked completed, and picks up at the first step that is not. Secrets already generated are loaded back from kindo-secrets-config instead of regenerated, so per-service passwords stay stable.
If the contract has changed since the last run, the CLI warns you:
Warning: Config changed since last run. Verify changes are intentional.This is the contract-hash drift warning — every install run stores a hash of install-contract.yaml + environment-bindings.yaml in the state ConfigMap’s meta.json. A mismatched hash on resume means you changed the inputs between runs. The install continues (config edits between runs are legitimate — e.g., fixing a bad Postgres password), but you should verify the diff is what you expect.
Re-run a single step by name — useful once you know which step is wedged:
kindo install --step applicationskindo install --step post-installkindo install --step hatchet-token--step runs outside the usual sequencing: it does not check --resume state, but it does update kindo-install-state for the named step on completion. Use it to surgically re-run post-install after editing the contract (for example, changing adminUser.email) without re-running migrations and applications.
7. State introspection
All install state lives in the cluster, not on your filesystem. Any operator with cluster access and the CLI can observe or resume an install.
kindo status
kindo statusPrints the install meta (version, contract hash, start time) and a per-step table:
Kindo Install State Version: 2026.04.1 Started: 2026-04-20T14:02:17 Contract hash: 7c0a3e...
Step Status Completed Errorgenerate-secrets ✓ completed 2026-04-20T14:02:35merge-bindings ✓ completed 2026-04-20T14:02:41setup-registry ✓ completed 2026-04-20T14:02:48db-bootstrap ✓ completed 2026-04-20T14:05:02peripheries → runningapplications ○ pendingkindo-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
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 -dThe CLI reads this Secret at the start of every command (except the first generate-secrets step on a fresh install) so no secret ever touches your local filesystem. Per-app Kubernetes Secrets are derived from this central blob at render time.
Managing integrations
Integrations (GitHub, Slack, Jira, CrowdStrike Falcon, and the rest of the MCP integration catalog) are managed outside the core install loop:
kindo integrations list # Show all available integrationskindo integrations enable github slack # Mark integrations enabled in the contractkindo integrations disable jirakindo integrations apply # Register enabled integrations with Nangokindo integrations apply runs after kindo install --apply completes and reuses the unified MCP server — no per-integration pods are deployed.