Migrations
Ory Talos has built-in database migrations. Run them before first use and after each upgrade.
Migrations are backward compatible: a new schema coexists with the previous binary, so Ory Talos commercial supports zero-downtime rolling upgrades. Apply the migration first, then roll the application replicas. Old replicas keep serving against the new schema until they're replaced.
Commands
# Apply all pending migrations
talos migrate up --database "sqlite:///var/lib/talos/data.db"
# Roll back the last migration (--steps defaults to 1)
talos migrate down --database "sqlite:///var/lib/talos/data.db"
# Roll back the last three migrations
talos migrate down --steps 3 --database "sqlite:///var/lib/talos/data.db"
# Check migration status
talos migrate status --database "sqlite:///var/lib/talos/data.db"
# Force a specific version after a partial migration left the schema dirty
talos migrate force 17 --database "sqlite:///var/lib/talos/data.db"
Upgrade workflow
- Back up the database. For PostgreSQL or CockroachDB, take a logical backup with
pg_dumporBACKUP INTO. For MySQL, usemysqldump. SQLite users copy the file. - Run migrations from a single instance. Apply migrations from one Job, init container, or operator — never from inside the application's startup path. See Production safety.
- Apply migrations. Run
talos migrate up. Watch stderr; the command exits non-zero on failure. - Resolve dirty state if needed. If a migration fails partway through,
migrate upandmigrate downrefuse to run and reportError: Database is in dirty state at version N. Inspect the schema, complete or revert the partial change by hand, then runtalos migrate force <version>to update the migration tracker. Never rerun on top of a dirty state. - Verify with
talos migrate status. Confirm the version matches the new binary's expected schema and thatDatabase Statusreportsclean, notDIRTY. - Roll the application. Start replicas of the new binary in a rolling deploy. You don't need to drain traffic, because the new schema stays compatible with the previous binary.
Production safety
- Run
talos migrate upfrom a Job, init container, or operator — never from inside the application's startup path. Concurrent migration runners deadlock or corrupt the migration tracker. - Pin the migration image to the same version as the application image. Don't use mutable tags (
latest,staging) for migration jobs. - The OSS image (
oryd/talos) ships only SQLite migrations and rejects any non-SQLite DSN. For PostgreSQL, MySQL, or CockroachDB, use theoryd/talos-commercialimage. Commercial migrations live undercommercial/persistence/migrations/{postgres,mysql,cockroach}/; SQLite migrations live underinternal/persistence/migrations/sqlite/.
DSN examples
# SQLite (OSS)
talos migrate up --database "sqlite:///var/lib/talos/data.db"
# PostgreSQL (commercial)
talos-commercial migrate up --database "postgres://talos:secret@db:5432/talos"
# MySQL (commercial)
talos-commercial migrate up --database "mysql://talos:secret@tcp(db:3306)/talos?parseTime=true"
# CockroachDB (commercial)
talos-commercial migrate up --database "cockroach://talos@crdb:26257/talos"
Kubernetes
Use a Job to apply migrations once, then start the application Deployment. The example below targets a SQL backend, so it uses
oryd/talos-commercial. For SQLite, use oryd/talos. Always pin the image to a specific released version, never latest.
apiVersion: batch/v1
kind: Job
metadata:
name: talos-migrate
spec:
backoffLimit: 0 # Don't retry — a failed migration leaves the schema dirty.
template:
spec:
containers:
- name: migrate
image: oryd/talos-commercial:<version-you-want> # Pin to the same tag as the application image.
args: ["migrate", "up"]
env:
- name: DB_DSN
valueFrom:
secretKeyRef:
name: talos-secrets
key: dsn
restartPolicy: Never
