Skip to content

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.

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 and a SHA256SUMS file.

ToolRequiredVersionPurpose
kubectlYesmatches clusterCluster access
helmYesv4 (latest)Chart deployment
helmfileYesv1.2+Release orchestration
yqYesv4+YAML processing
PythonYes3.11+CLI runtime (installable via uv)
uvRecommendedlatestPython + package manager in one binary
psqlOptionalanykindo db tunnels and prompts
Registry credsYesregistry.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:

Terminal window
python3 --version

If 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.

Terminal window
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
uv --version

Log in to the Kindo registry, then pull and unpack the artifact at your target release version:

Terminal window
helm registry login registry.kindo.ai --username <registry-username>
helm pull oci://registry.kindo.ai/kindo-cli/kindo-cli --version <release> --untar

Verify the wheel checksums before installing:

Terminal window
(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.

Recommended — via uv (isolates the CLI in its own environment):

Terminal window
uv tool install --force ./kindo-cli/kindo_cli-*.whl

Alternative — via pip (if you already have Python 3.11+):

Terminal window
python3.11 -m pip install ./kindo-cli/kindo_cli-*.whl

The wheel filename is PEP 440-normalized (release 2026.03.4 produces kindo_cli-2026.3.4-py3-none-any.whl), which is why the commands above match it with a glob instead of spelling out a version.

To upgrade, pull the new release and re-run the same install command.

Terminal window
kindo --version
kindo --help

If 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).

The config wizard produces the two files every subsequent command reads:

  • install-contract.yamlwhat to deploy (version, base domain, registry credentials, admin user, applications, peripheries, secrets-store backend, optional SMTP/models/integrations).
  • environment-bindings.yamlwhere 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:

Terminal window
kindo config init

The wizard walks through eleven sections. Draft state is saved to .kindo-config-draft.json after each section, so you can quit with Ctrl-C and resume later.

  1. Deployment Basics — base domain, application version, storage class.

  2. Container Registryregistry.kindo.ai username and password (provided by Kindo).

  3. Initial Admin User — admin email, display name, SSO domain, and whether to enable the bootstrap username/password login for first access.

  4. PostgreSQL — main admin connection string and an auxiliary admin connection string (same instance is fine for small deployments).

  5. Redis — connection string for the standalone Redis.

  6. RabbitMQ — connection string for RabbitMQ (AMQP or AMQPS).

  7. Object Storage — S3-compatible bucket name, region, optional static access/secret keys, optional custom endpoint for MinIO or GCS.

  8. SMTP (optional) — host, port, credentials, and From address if you want Kindo to send email.

  9. 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.

  10. Secrets Store — which backend stores application secrets: kubernetes (plain in-cluster Secrets, the default), azurekv (Azure Key Vault), vault (HashiCorp Vault), or aws (AWS Secrets Manager). External backends prompt only for that backend’s connection and auth fields and reference auth prerequisites you create on your side (for example workload-identity federation or a bootstrap Secret).

  11. Integrations (optional) — toggle MCP integrations now or add them later with kindo integrations enable <name>.

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 instead of starting fresh.

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 strings
Full example install-contract.yaml
apiVersion: kindo.ai/v1
metadata:
schemaVersion: '1'
baseDomain: marcos.kin.do
registry:
# Defaults to registry.kindo.ai. Override `host` to pull from a
# different OCI registry (e.g. local k3d, an internal mirror); charts
# and images both resolve from this single value. `username`/`password`
# are optional — empty values skip `helm registry login` (k3d, public
# mirrors).
host: registry.kindo.ai
username: robot$aws_demo
password: REDACTED
appVersion: 2026.03.15
storageClass: gp2
# Global application log level: debug | info | warn | error. Omit to leave
# each app on its own default. Change it later without a full upgrade via
# `kindo config log-level set <level>`.
logLevel: info
applications:
api: true
next: true
litellm: true
credits: true
courier: true
cerbos: true
ssoready: true
prisma-migrations: true
hatchet: true
nango: true
task-worker-ts: true
mcp-unified: false
mcp-platform: true
sandbox: false
peripheries:
unleash: true
qdrant: true
presidio: true
speaches: true
otel-collector: true
clickhouse: true
hyperdx: true
bootstrapAdmin:
enabled: true
password: CHANGE_ME
adminUser:
name: Admin User
ssoDomain: 'kindo.ai'

mcp-platform is enabled by the Helmfile default when it is omitted from the contract. Set applications.mcp-platform: false to opt out of the Kindo-managed agent tools adapter. The adapter is installed by default, while Platform tool availability is enabled separately as the managed rollout proceeds.

Full example environment-bindings.yaml
apiVersion: kindo.ai/v1
postgres:
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-verify
redis:
connectionString: redis://kindo-production-redis.TEST.ng.0001.usw2.cache.amazonaws.com:6379
rabbitmq:
connectionString: amqps://rabbitmq_admin:[email protected]
storage:
bucketName: kindo-uploads
region: us-west-2
accessKey: REDACTED
secretKey: NANqUEdAH0REDACTED

Before running any install step, prove that your cluster can actually talk to the dependencies listed in environment-bindings.yaml.

Terminal window
kindo config validate --preflight

Plain kindo config validate only checks schema. Adding --preflight does more:

  1. Creates the kindo-system namespace if missing.

  2. Uploads your environment-bindings.yaml as a Kubernetes Secret (kindo-preflight-config).

  3. Creates the image-pull secret for registry.kindo.ai and performs a helm registry login.

  4. Deploys the preflight Helm chart from oci://registry.kindo.ai/kindo-helm/preflight into kindo-system, enabling cluster-side checks.

  5. Waits up to two minutes for the kindo-preflight Job 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 storageClass resolves in the cluster.
  • cert-manager — present when TLS is expected.
  • DNSbaseDomain and its derived subdomains resolve.
  • Ingress controller — at least one IngressClass is available.

--plan is a read-only operation that prints exactly what the installer will do without touching the cluster:

Terminal window
kindo install --plan

It 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.

Once the plan looks right, run the install:

Terminal window
kindo install --apply

The 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.

#StepPurpose
1generate-secretsGenerate passwords, API keys, and encryption keys; persist in-cluster.
2merge-bindingsMerge environment-bindings.yaml + contract values into the secrets config.
3setup-registryCreate registry-credentials image-pull secrets in every app namespace.
4external-secretsInstall the External Secrets Operator (operator, CRDs, validating webhook) from the registry, then wait for the CRDs to be established and the webhook to serve. If the cluster already runs its own ESO serving external-secrets.io/v1, the installer skips its own installation, logs that, and proceeds; an ESO too old to serve that API version fails the step with upgrade guidance.
5db-bootstrapCreate per-service Postgres databases and service users.
6peripheriesDeploy Unleash (+ Edge), Qdrant, Presidio, Speaches, Hatchet, the OTel Collector, and the rest of ClickStack — ClickHouse (+ Keeper, chproxy) and HyperDX (see Observability). Pre-creates the hatchet secret.
7hatchet-tokenRead Hatchet’s boot-time client config and store the token in the secrets config.
8migrationsRun Prisma migrations via the prisma-migrations release.
9applicationsDeploy the Kindo application services enabled in install-contract.yaml and by Helmfile defaults — typically api, next, litellm, credits, cerbos, ssoready, nango, task-worker-ts, and mcp-platform (the Kindo-managed agent tools adapter); optional services include mcp-unified, sandbox, and courier — the Slack protocol sidecar handling both inbound event parsing and outbound replies.
10post-installBootstrap admin user, SSO, Nango, models, and feature flags from the contract.
11integrationsDeploy the integrations-reconciler release, whose reconcile job registers integrations with Nango and reconciles IntegrationConfig metadata, push OAuth credentials to Nango, then deploy legacy MCP releases when needed. Skips when no integrations are enabled. Ok-to-fail: a failure here is recorded and reported but does not abort the install — the platform is already up, and the reconcile is re-run on its own 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.

After a failure (or an intentional Ctrl-C), resume with:

Terminal window
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:

Terminal window
kindo install --step applications
kindo install --step post-install
kindo 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.

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.

Terminal window
kindo status

Prints 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 Error
generate-secrets ✓ completed 2026-04-20T14:02:35
merge-bindings ✓ completed 2026-04-20T14:02:41
setup-registry ✓ completed 2026-04-20T14:02:48
db-bootstrap ✓ completed 2026-04-20T14:05:02
peripheries → running
applications ○ pending

State is stored in the kindo-install-state ConfigMap in the kindo-system namespace:

Terminal window
kubectl -n kindo-system get cm kindo-install-state -o yaml

Keys:

  • meta.json{ version, contract_hash, started_at }
  • step.<name>{ status, started_at, completed_at, error } for each step, where status ∈ { 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.

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:

Terminal window
kubectl -n kindo-system get secret kindo-secrets-config -o jsonpath='{.data.secrets\.yaml}' | base64 -d

The 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.

Integrations (GitHub, Slack, Jira, CrowdStrike Falcon, and the rest of the MCP integration catalog) are managed outside the core install loop:

Terminal window
kindo integrations list # Show all available integrations
kindo integrations enable github slack # Mark integrations enabled in the contract
kindo integrations disable jira
kindo integrations apply # Register enabled integrations and reconcile metadata

kindo integrations apply runs after kindo install --apply completes, and can be re-run any time the contract’s integrations change — it is idempotent. It deploys the integrations-reconciler Helm release, whose reconcile job registers the selected integrations with Nango and reconciles their IntegrationConfig metadata against the release’s integration catalog, marking each explicitly selected integration as verified. The CLI waits for that job to complete (dumping its logs if it fails), then pushes any OAuth client credentials from the contract directly to Nango. Unified MCP integrations reuse the shared server; only legacy MCP integrations deploy per-integration pods.