Upgrading
An upgrade is a new binary or image plus, usually, database migrations. The binary carries its migrations; you apply them as the schema owner, then roll the servers. Nothing else changes: the master key, Valkey and your configuration stay as they are unless the release notes say otherwise.
Versions and what they promise
Releases follow Semantic Versioning. From 1.0, a patch or minor release upgrades in place with no configuration changes; a major release may need some, and its notes say what. Before 1.0, a minor release (0.1 to 0.2) may break compatibility (a renamed setting, a changed admin API field), and its notes say how. A patch release never does.
Every release’s notes, on its GitHub release and in
CHANGELOG.md, have an
Upgrade notes heading whenever there is something to do beyond replacing the binary,
including a release whose migrations are not compatible with the release before it
(next section). Read the notes of every release between yours and the target.
Rolling upgrades and the one-release rule
During a rolling upgrade the old release keeps serving on the migrated schema until its last node is replaced; the Helm chart and the compose stack both migrate first and roll second. That only works if the migrations leave the old code working, so the rule for rIDM’s own migrations is:
A release’s migrations keep the release immediately before it working. They add tables, columns with defaults and indexes; they do not drop, rename or retype what the previous release reads or writes. A removal happens in two releases: the first stops using the thing, the next drops it.
The same holds for what nodes share in Valkey (sessions, flows in progress, cached documents) and for the cache invalidation messages they exchange: old and new nodes read each other’s entries during the roll.
A release that has to break the rule says so under Upgrade notes, and then the upgrade is stop-the-world: scale to zero, migrate, start the new release.
Because the rule covers one step, skipping releases (1.2 straight to 1.5) is also a
stop-the-world upgrade: migrations are cumulative, and migrate applies all of them in
order, but a 1.2 node is not promised to work on the 1.5 schema. Stop every old node
first, or upgrade one release at a time with a rolling upgrade each.
Every pull request checks the rule. deploy/upgrade-smoke/run.sh, in CI’s packaging
job, starts the previous release’s image on the compose stack and bootstraps an
administrator. It then upgrades the same volumes to the image under test, which
migrates, starts, answers /readyz and still publishes every signing key the old release
published. Last, it starts the previous image again on the migrated database, as it would
serve during a roll. A release whose upgrade notes break the rule runs it with
ALLOW_BREAKING_SCHEMA=1, which skips that last step. To run it locally:
docker build -f api/Dockerfile -t ridm:ci .
TO_IMAGE=ridm:ci deploy/upgrade-smoke/run.sh # FROM_IMAGE=… to pick the old image
Before you upgrade
- Read the release notes, back to the release you run.
- Take a backup of the database, or check that point-in-time recovery covers the present (Backup and restore). A migration cannot be undone any other way.
- Verify the release you are about to run (Releases and verification), and pin it by version or, better, by image digest.
- Try it on a copy first when the notes list migrations on large tables: restore last night’s backup into a staging environment and time the migration there. Most migrations take milliseconds; one that builds an index on a table of millions of rows takes as long as that index takes, and holds its locks meanwhile.
Upgrading
Helm
helm upgrade ridm oci://ghcr.io/zerosandonesllc/charts/ridm --version 1.2.3 -f values.yaml
The chart’s pre-upgrade hook runs ridm-api migrate in a Job as the migrator role.
The Deployment rolls only after the Job succeeds, with maxUnavailable: 0, so capacity
never drops; if the migration fails, the running pods are untouched and helm upgrade
reports the failure. See Kubernetes (Helm).
docker-compose
Set the new image in .env (RIDM_IMAGE=ghcr.io/zerosandonesllc/ridm:1.2.3, or
…@sha256:…), then:
docker compose pull
docker compose up -d
The one-shot migrate service runs first with the new image and the server is
recreated only if it succeeds. The stack is one node, so requests fail for the few
seconds the server takes to restart. See
Production with docker-compose.
Static binaries and other setups
- Install the new binaries next to the old ones.
- Run
ridm-api migrateonce, withDATABASE_URLset to the migrator role. It takes an advisory lock, so a second run started by mistake waits and then finds nothing to do. - Restart the nodes one at a time with the new
ridm-api, waiting for/readyzon each before the next. The server finishes in-flight requests for up to 20 seconds afterSIGTERM.
With MIGRATE_ON_START=true and the server running as the schema owner, the first new
node migrates as it starts; see Container image for
why the separate step is preferred.
After the upgrade
/readyzanswers200on every node, and/healthzreports the newversion.- The start-up log has no
database migrations are pendingwarning. - Sign in to the admin console, and let a client get a token.
- Watch the error rate and the job metrics for an hour (Observability).
Rolling back
Migrations are forward-only: there are no down migrations. What a rollback takes depends on what the new release migrated.
-
No migrations in between: run the previous release again. Nothing else changed.
-
Migrations that kept the one-release rule: the previous release runs on the new schema, as it did during the roll, so running it again works. It logs a warning at start-up:
WARN the database has migrations this release does not know: a newer release migrated it. Run that release, or restore the backup taken before the upgrade count=1 newest=20270301120000The warning stays until you upgrade again. Its
ridm-api migraterefuses to run (migration 20270301120000 was previously applied but is missing in the resolved migrations), which is correct: there is nothing it could apply. Upgrade again once the problem is fixed, rather than staying on the older release indefinitely. -
Migrations the notes marked incompatible, or a skip over several releases: the previous release is not promised to work on the new schema. Restore the backup taken before the upgrade (Backup and restore), with the consequences listed there for everything since.
Never restore a backup made by a newer release and run an older release on it; that is the same as the last case.
The master key and signing keys
Upgrades do not touch either. Rotating the master key is a separate, online operation (Rotating keys); do not combine it with an upgrade, so that a problem in one is not mistaken for the other. Each node checks at start-up that the master key decrypts the database’s signing keys and refuses to start if not, so a node that comes up after an upgrade with a mistyped secret stops with a clear message instead of failing sign-ins.