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.

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

ToolRequiredVersionPurpose
kubectlYesmatches clusterCluster access
helmYesv3.12+Chart deployment
helmfileYesv0.159+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)

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

Step 2: Install the Kindo CLI

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

Terminal window
uv tool install --python 3.11 "<cli-wheel-url>"

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

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

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

2. Generate config with kindo config init

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

  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. 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 strings
Full example install-contract.yaml
apiVersion: kindo.ai/v1
metadata:
schemaVersion: '1'
baseDomain: marcos.kin.do
registry:
username: robot$aws_demo
password: REDACTED
appVersion: 2026.03.15
storageClass: gp2
applications:
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: false
peripheries:
unleash: true
qdrant: true
presidio: true
speaches: true
bootstrapAdmin:
enabled: true
password: CHANGE_ME
adminUser:
name: Admin User
ssoDomain: 'kindo.ai'
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

3. Preflight your infrastructure

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.

4. Preview with kindo install --plan

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

5. Apply with kindo install --apply

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.
4db-bootstrapCreate per-service Postgres databases and service users.
5peripheriesDeploy Unleash (+ Edge), Qdrant, Presidio, Speaches, Hatchet (pre-creates hatchet secret).
6hatchet-tokenRead Hatchet’s boot-time client config and store the token in the secrets config.
7migrationsRun Prisma migrations via the prisma-migrations release.
8applicationsDeploy 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).
9post-installBootstrap 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:

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.

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

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

kindo-install-state ConfigMap

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.

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:

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.

Managing integrations

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 with Nango

kindo integrations apply runs after kindo install --apply completes and reuses the unified MCP server — no per-integration pods are deployed.

Next