Skip to main content
Umami runs as its official image, unmodified, pinned by tag and index digest (ghcr.io/umami-software/umami:3.5.0@sha256:b64f4040...a5dbc in infra/ansible/vars/pins.yml). A new version is always a deliberate change: Umami migrates its own database (Prisma migrations) when it starts, and those migrations only go forward. So the rollback is a database restore, never just the old image. For a security fix inside the 24-hour target, follow security-patch.md; it uses the same upgrade gate and adds triage and mitigations. This page is the general procedure plus the rollback.

Upgrade

  1. Gate it on the development stack (needs make dev-up, Docker, Node 26 with pnpm and the Playwright browsers, Go 1.27.4 on PATH):
    scripts/umami-upgrade.sh resolves the index digest the registry serves for that version, moves the pin in deploy/compose/dev.yml, infra/ansible/vars/pins.yml, tests/observability/compose.yml and docs/reference/umami-contract.md, recreates the dev stack so Umami migrates the existing database, then runs umami-compat, the isolation gate, console-e2e and webpath, stopping at the first failure. Each step prints step <name>: ok in <n> s. A new version stays red at umami-compat until someone has reviewed its OpenAPI routes against the Caddy deny list and updated tests/umami-compat/testdata/openapi-routes.txt. Commit the diff only when every gate passed.
  2. Release. Merge, tag v<release>; release-images.yml builds, scans and signs the platform images and attaches release-images.json. Umami itself is an upstream image and is pulled by its pinned digest.
  3. Take a dump of the Umami database before the rollout. On the core node that holds the Patroni leader (overview.md):
    Write down the time and the migration line (for 3.5.0: 27 migrations, last 27_add_error_tracking). The point-in-time backups also cover this moment, but a dump of one database is far quicker to restore.
  4. Roll out to staging, then production:
    deploy.yml drains and updates one node at a time and stops at the first unhealthy node. The new Umami migrates the database on its first start on the first core node.
  5. Verify: ../deploy/verification.md steps for the core tier, UmamiHeartbeatFailing stays quiet, a tenant dashboard opens through the console’s SSO, and WEB_EVENTS drains (queue-backlog-and-dlq.md).

Rollback

Use it when the new version misbehaves in a way a fix-forward cannot address in time.
  1. Stop delivery into Umami on both core nodes, so nothing more is written to the database you are about to replace. Accepted events wait in WEB_EVENTS (72 hours):
    Run it on both core nodes. The list names every Umami process: umami plus umami-2 up to umami-<core_umami_processes> (4 by default; sudo docker compose ps shows them). Tenant web dashboards are down from here until step 5.
  2. Restore the dump on the leader’s node:
    Then recreate the retention role’s grants, which live in the Umami database: run platformctl db umami-retention-role as in overview.md.
  3. Check the migration level matches what step 3 of the upgrade recorded:
  4. Deploy the previous release, whose pins.yml names the previous Umami digest. In your working copy check out that release’s tag, then:
    The deploy starts Umami and the replayer again.
  5. Verify as in upgrade step 5. The replayer delivers what queued since step 1.
What a rollback loses: events the replayer stored in Umami between the dump (upgrade step 3) and rollback step 1. They were acknowledged and removed from WEB_EVENTS, so nothing re-sends them. Keep the time between the dump and the decision short, and tell the tenants which window is affected. The platform database (control plane, API metrics) is not touched by this rollback.

Exercised

Not yet exercised: the rollout and rollback through deploy.yml on staging (inputs I4, I5, I6, I13, I14, I15), the dump on a Patroni leader, and the retention role step after a restore. The phase 19 rehearsal used plain containers with the same images and the same pg_dump and pg_restore commands.