Docs Self-hosting & operations
Self-hosting & operations
Database migrations
Every Kumo.io release brings its database schema with it. Migrations only move forward, are checksummed, and take a lock, so they are safe to run on every start — or as a separate release step if you prefer.
When migrations run
You rarely run migrations by hand:
| Installation | When the schema is migrated |
|---|---|
| Single container | On every start, before Kumo.io serves anything |
kumo deployment on Docker Compose | By kumo deployment apply, in a one-time bootstrap step before the containers start |
| AWS stack | By a one-shot migrate task inside your VPC, on every kumo deployment apply |
The API, worker and scheduler roles also migrate on start by default. Running migrations again when nothing is pending does nothing.
What makes them safe
- Forward-only. There are no down migrations. To undo a release, restore the backup you took before upgrading and start the previous version — see Backups and restore.
- Checksummed. Kumo.io records a SHA-256 of every migration it applies. If an applied migration's file has changed, Kumo.io stops and reports it as drifted instead of letting your schema silently diverge.
- Locked. A whole migration run holds a PostgreSQL advisory lock, so two processes starting at once take turns instead of racing.
- One transaction per migration. A migration that fails rolls back and is not recorded, so you can fix the cause and run it again.
Check the schema without changing it
kumo db migrate --validate
It reports what is applied, pending and drifted, and changes nothing:
Schema up to date — N applied, 0 pending, 0 drifted.— exit code 0.Schema NOT up to date — N pending, M drifted.— exit code 1, with onependingorDRIFTEDline per migration.
On the single container, run it with docker exec kumo kumo db migrate --validate.
Run migrations yourself
kumo db migrate
It applies everything pending and prints Migrations OK — applied N new migration(s). with one applied line each, then exits 0. If drift blocks the run, it prints Migrations FAILED — … with the drifted versions and exits 1.
kumo db migrate reads the database connection from KUMO_PG_DSN, or from --dsn.
Migrate as a separate release step
If you'd rather migrate once per release than on every start — for example with several API containers — run kumo db migrate as a one-off step, then start each server role with --no-auto-migrate:
kumo db migrate
kumo serve tenant-api --no-auto-migrate
kumo serve worker --no-auto-migrate
kumo serve scheduler --no-auto-migrate
The exit codes are made for release scripts: stop the rollout if kumo db migrate exits non-zero.
An older version against a newer schema
If a running Kumo.io finds migrations in the database that it doesn't know about — an older container still running during an upgrade, or after a rollback — it logs a warning that the database may be newer than the code, and keeps running. Kumo.io's schema changes are designed to be additive first, so that a schema one release ahead stays compatible with the release before it.
The built-in database's PostgreSQL version
The single container's built-in database is PostgreSQL 17. If its data on the /data volume was created by a different PostgreSQL major version, the container refuses to start and says so, rather than risk the data.