Docs Self-hosting & operations
Self-hosting & operations
Run Kumo.io as separate services
Describe your installation in a short deployment spec, and kumo deployment renders it into a Docker Compose project, converges it, and keeps it converged on every re-run.
When to use this
Use kumo deployment when one container is not the shape you want — for example to run the API, worker and scheduler as separate containers you can scale independently, or to keep the database in its own container. If one container is enough, run Kumo.io in one container instead; it needs no spec.
You run kumo deployment on the machine that runs Docker, with the Kumo.io CLI and Docker Compose v2 installed. Every container it starts runs the same kumoio/kumo image.
How it works
A deployment spec is a YAML file describing the installation you want: the image, where the database lives, which roles run and how many of each. kumo deployment compares that spec with what is actually running and makes the difference:
kumo deployment validatechecks the spec without touching Docker.kumo deployment planshows what would change, and changes nothing.kumo deployment applyconverges the running installation to the spec.
The Compose file is generated from the spec every time — never edit it by hand. Apply holds a lock while it runs and records each step, so an interrupted apply resumes where it stopped when you run it again.
Choose a topology
| Topology | What runs | Database |
|---|---|---|
| All-in-one | One appliance container (app, API, admin API, plus a job worker) | A PostgreSQL container on a named volume |
| All-in-one, your database | One appliance container | Your PostgreSQL, from a connection string |
| Split roles | tenant-api, admin-api, ui, worker and scheduler containers | A PostgreSQL container on a named volume |
In the split topology, raise a role's replicas to run more of it. Only one scheduler is active at a time, so one replica is enough.
Only the all-in-one appliance role can connect to your Kumo.io account, and a self-hosted installation stays restricted until it is connected or licensed. A split-role installation can be unlocked only with a license file.
Licenses for self-hosted installations are not available yet, so use an all-in-one topology today.
Write the spec
This spec runs the all-in-one topology on a bundled PostgreSQL container:
apiVersion: deployment.kumo.io/v1alpha1
kind: Deployment
metadata:
name: appliance
spec:
policy: production
target:
driver: compose
namespace: kumo
compose:
image: kumoio/kumo:<version>
release:
version: <version>
digest: 'sha256:<image-digest>'
storage:
backend: postgres
mode: managed-local
topology:
profile: appliance
roles:
appliance:
replicas: 1
bootstrap:
systemSeed: release
templates: release
upgrade:
migrations: automatic
backup: required
maximumUnavailable: 1
observability:
metrics: enabled
logs: stdout
To use your own database, change storage to:
storage:
backend: postgres
mode: external
dsnRef: 'env://KUMO_PG_DSN'
For split roles, set topology.profile: split and list the roles:
topology:
profile: split
roles:
tenant-api:
replicas: 1
admin-api:
replicas: 1
ui:
replicas: 1
worker:
replicas: 1
scheduler:
replicas: 1
You can also scaffold a spec with kumo deployment init --name <name> --profile appliance --out <file> and edit it. A scaffolded spec is fully written out, so a later Kumo.io version can never change your installation by changing a default. The profiles are local-dev, appliance and aws-fargate. There is no Kubernetes driver yet, so init offers no Kubernetes profile and refuses --driver kubernetes.
Pin the image
A spec with policy: production must pin the image digest, and apply checks that the image on the machine matches it. Pull the image, then read its digest:
docker pull kumoio/kumo:<version>
docker image inspect --format '{{index .RepoDigests 0}}' kumoio/kumo:<version>
Put the sha256:… part in release.digest.
The production policy also requires durable storage and upgrade.backup: required. kumo deployment validate tells you exactly which rule a spec breaks.
Put secrets in the sidecar env file
Secrets never go in the spec or the generated Compose file. Every Kumo.io container reads an optional env file named after the spec's namespace — kumo.env for the spec above — next to the generated Compose file. The generated file's header names it; run kumo deployment render --file <spec> --out <dir> to see exactly what apply will run.
| Variable | When |
|---|---|
POSTGRES_PASSWORD | Bundled database: the password for the PostgreSQL container |
KUMO_PG_DSN | Your own database: its connection string (required) |
KUMO_AUTH_TOTP_KEY | The key that protects two-factor secrets (32 random bytes, base64url). Unset, each container creates its own; set it so every Kumo.io container shares one |
KUMO_ADMIN_API_TOKENS + KUMO_ADMIN_ROLES | The admin API credential — set both, or neither |
If you set neither admin variable, Kumo.io creates an admin credential on first boot and prints it once in the container log, exactly as the single container does. If you set only KUMO_ADMIN_API_TOKENS, the admin API refuses to start; if you set only KUMO_ADMIN_ROLES, no token can sign in to it.
The credential-store encryption key needs no configuration: Kumo.io creates it in the database on first boot. Set KUMO_PG_SECRETS_KEK in the env file only if you want to hold that key outside the database. Every other setting is on the configuration reference.
Apply it
kumo deployment plan --file kumo.deployment.yaml
kumo deployment apply --file kumo.deployment.yaml --yes
--yes is required for a policy: production spec. Apply checks Docker is running, verifies the image, starts the database and waits for it to be healthy, runs a one-time bootstrap (schema migrations, system data and the built-in view templates), starts the Kumo.io containers, and waits until every one is running. It ends with an Access section listing the addresses.
Kumo.io's ports are published on 127.0.0.1 only:
| Port | What |
|---|---|
| 9500 | The app, and sign-in |
| 9501 | The tenant API |
| 9503 | The admin API |
To serve your team, put a TLS-terminating reverse proxy in front of port 9500 and set KUMO_EXTERNAL_URL in the env file to the public address.
Then connect the installation to your Kumo.io account.
Day two
| Command | What it does |
|---|---|
kumo deployment status --file <spec> | Health and convergence at a glance |
kumo deployment plan --file <spec> --detailed-exit-code | Exits 3 when the running installation has drifted from the spec |
kumo deployment doctor --file <spec> | Read-only diagnosis of the target, release and roles; exits 3 when something is blocked |
kumo deployment config show --file <spec> | The fully resolved configuration, with secrets redacted |
kumo deployment operation list --file <spec> | Past operations; operation watch <id> follows one |
kumo deployment lock show --file <spec> | Who holds the apply lock |
To change anything — scale a role, upgrade the image — edit the spec and run apply again. See Upgrades.
Back up before you upgrade
On Docker Compose, Kumo.io's data lives in named Docker volumes and apply does not snapshot them for you. Take a database backup before every upgrade — see Backups and restore.
Remove an installation
kumo deployment destroy --file <spec> --allow-destroy --yes
This stops and removes the containers but keeps the volumes, so applying the spec again brings the same data back. To delete the volumes too, add --allow-data-loss. Without it, no data is ever deleted.