Upgrades and migrations
Order matters. Read this before pulling.
The rule
Migrations are applied before the new containers start.
New code expects the new schema. With the bundled compose file this happens
on its own: a migrate service applies whatever is pending and exits, and
the api and worker wait for it to succeed. If a migration fails, they do not
start — docker compose logs migrate says why.
Upgrading
# 1. Back up first. Always.
./scripts/backup.sh
# 2. Pull the new images
docker compose pull
# 3. Start the new containers (migrations run first)
docker compose up -dRunning your own compose file or orchestrator? Apply migrations yourself before starting the new api and worker:
./services/postgres/migrate.sh --apply # from a checkout
# or, with only the postgres image:
docker run --rm -e PGHOST=... -e PGUSER=... -e PGPASSWORD=... -e PGDATABASE=... \
--entrypoint bash <postgres image> /opt/lenshub/migrate.sh --applyChecking what will happen
./services/postgres/migrate.sh --statusLists every migration as applied or pending, without changing anything.
Migrations are almost always additive
They add tables, columns and indexes and do not drop your data, which makes
most of them safe to apply while the old version is still running. The
exceptions say so at the top of the file — for example, removing a column
nothing reads any more (055_drop_contexts_visibility.sql) breaks an older
worker still running against the database, so start the new containers
straight after.
Some build indexes concurrently, which takes minutes on a large database but
does not block reads or writes. None rewrite a large table under a lock: a
new column that needs values for existing rows is filled in the background
in small batches (for example context_versions.body_hash, which the worker
backfills after 054).
After upgrading
docker compose ps # everything healthy
curl http://localhost:8080/readyz # {"status":"ok"}Then confirm a search still returns results — schema and code agreeing is necessary but not sufficient.
Rolling back
Most migrations are additive, so the previous version generally runs against the newer schema without complaint. Restore from a backup if it does not (see Backups).
Upgrade a staging instance first if you have one. If you do not, take the backup in step 1 seriously — it is the whole rollback plan.