Skip to content
BurnerByte

Upgrading

How to move between releases: what the version numbers promise, the order the containers have to come up in, and the one thing that is not reversible.

Upgrades are forward-only in practice. Read that sentence before the rest of the page: there is a make migrate-down, it is not a rollback plan, and the thing that gets you back to yesterday is a restore.

What a Version Number Promises

Releases are tagged vMAJOR.MINOR.PATCH and the changelog is the record of what changed in each.

ChangeLands inWhat it means for you
feat:a minor releaseNew capability. Migrations likely.
fix:, docs:, perf:a patch releaseNo new schema in the normal case.
Breaking changea minor release, called out at the top of its changelog entryRead the entry before upgrading.

The HTTP API is versioned separately under /api/v1 and does not move with the release number. A minor release does not remove an endpoint or a response field.

Note

Before You Start

  1. Read the changelog entry for every release you are skipping

    Not just the one you are moving to. Going from v1.21.0 to v1.24.0 means the breaking changes in 1.22 and 1.23 apply to you too, and each is called out in its own entry rather than repeated in the latest one.

  2. Take a backup, and know that you can restore it

    bash
    docker compose exec -T postgres \  pg_dump -U burnerbyte -d burnerbyte --format=custom --no-owner \  > pre-upgrade-$(date +%Y%m%d).dump

    Backup and Restore is the full procedure, including the object store. For a patch release the dump alone is usually enough; for a minor release take both.

  3. Note the version you are on

    bash
    curl -s localhost:8080/api/v1/admin/version -H "Authorization: Bearer $TOKEN"

    Or read it off Settings → System. You need it to know which changelog entries apply, and to know what to go back to.

Docker Compose

bash
git fetch --tagsgit checkout v1.24.0docker compose build --build-arg VERSION="$(git describe --tags --abbrev=0)"docker compose up -d

docker compose up -d runs the migrate service to completion before api and smtpd start, because both declare service_completed_successfully on it. That ordering is the upgrade: schema first, then the binaries that expect it.

Confirm the migration step actually succeeded rather than assuming it:

bash
docker compose logs migrate | tail -20

migrate Exited (0) is success. A non-zero exit leaves api and smtpd un-started rather than running against a half-migrated schema, which is the behaviour you want — but it means "the site is down" and "the migration failed" look the same from outside.

Pass VERSION or the build reports `dev`

From Source

bash
git fetch --tags && git checkout v1.24.0make migrate-upmake build

Restart api and smtpd yourself, however you supervise them. Same order: migrations, then binaries.

Version Skew

During a rolling restart the two binaries are briefly on different releases, and the schema is already on the new one. That is safe in one direction only: migrations add before they remove, so an old binary against a new schema keeps working, while a new binary against an old schema does not.

Which gives the rule: migrate first, always, and never start a new api against a schema that has not been migrated. Compose enforces this for you. From source, nothing does.

If you run more than one smtpd, restart them one at a time — inbound mail is retried by the sending server, so a few seconds of refused connections costs nothing. Remember that only smtpd scales out; see Architecture.

Rolling Back

`make migrate-down` is not a rollback

To go back to a previous release:

  1. Check out the old tag and rebuild, without running migrations.
  2. If the new release added only additive migrations — new tables, new nullable columns — the old binaries run against the new schema without complaint, and you are done.
  3. If it did not, restore the dump you took before the upgrade. That is the only path back, and it costs you everything written since.

The changelog entry says which of those two you are in. If it does not say, assume the second.

Verifying

Beyond "the pages load":

bash
curl -fsS localhost:8080/healthz && echo okcurl -fsS localhost:8080/readyz  && echo ready

/readyz is the one that matters: it fails while the database or Redis is unreachable, which is what a half-finished upgrade looks like.

Then send one mail to a live inbox and watch it arrive without refreshing. That exercises smtpd, the database, the object store and the WebSocket bridge in a single action — which is most of what an upgrade can break.