Upgrading
Update Minutd with two commands, pin a version, roll back, and back up first.
Update
docker compose pull
docker compose up -d
That is the whole update. Compose pulls the newest image of the release line you follow and recreates the containers that changed. The database and the audio volume are untouched, and new database migrations run by themselves when the containers start.
The release notes and the changelog are on the releases page. Read them before a minor update, especially before 1.0.
Pick a release line
The compose file reads MINUTD_TAG from your .env and defaults to 0.1:
| You set | You follow |
|---|---|
MINUTD_TAG=0.1 | Every 0.1.x patch release. Moving to 0.2 is a decision you make. |
MINUTD_TAG=1 | Every 1.x.y release, once 1.0 is out. Breaking changes only come in 2.0 |
MINUTD_TAG=0.1.3 | Exactly that release. Nothing changes until you edit the line. |
Before 1.0 a minor version may contain breaking changes, so pin the minor (0.1, 0.2). From 1.0 on, pin the major (1) and expect only compatible changes inside it. Every release is published as X.Y.Z, X.Y, X and latest. Avoid latest for anything you care about.
All four images (minutd-web, minutd-worker, minutd-bot, minutd-whisper) share one version number, and the tag applies to all of them. Run them at the same version.
Back up first
Take a backup before every minor update and before anything you are unsure about. It is two commands:
docker compose exec -T db pg_dump -U minutd -Fc minutd > minutd-$(date +%F).dump
docker compose run --rm --no-deps --user root -v "$PWD:/backup" --entrypoint tar web czf /backup/minutd-data-$(date +%F).tgz -C /data .
The first is the database, the second the audio volume. Keep MINUTD_SECRET with them: without it stored keys and tokens cannot be decrypted.
Restore the database like this (this replaces what is there):
docker compose stop web worker bot
docker compose exec -T db dropdb -U minutd --if-exists minutd
docker compose exec -T db createdb -U minutd minutd
docker compose exec -T db pg_restore -U minutd -d minutd < minutd-2026-01-01.dump
How migrations run
Every process (web, worker) applies pending database migrations when it starts. A Postgres advisory lock makes this safe when several containers start together: one applies the migrations, the others wait and find nothing to do.
Migrations only go forward. Minutd does not ship down migrations. Before it applies anything it checks that the database is not newer than the app, and if it is, it refuses to start with this message:
The database was migrated by a newer version of Minutd (1 migration this version does not know). Refusing to start so nothing is damaged. Run the newer version again, or restore the database backup taken before the upgrade.
The containers then restart in a loop until you fix the version. Nothing in the database is touched.
Migrations that change existing data are written to be safe to run on a populated database, but a backup is the only real safety net. That is why the backup comes first.
Roll back
Rolling back to an older image is only possible when no migration ran in between.
- Patch update that had no migration: set
MINUTD_TAGback and rundocker compose pull && docker compose up -d. - Update that ran a migration: the old version will refuse to start, because the database is newer. Restore the database dump taken before the update, then start the old version. Anything recorded after the backup is lost.
If you did not take a backup you cannot roll back safely. Move forward to the next fixed release instead.
Automatic updates
Minutd does not update itself. If you want that anyway, the repository ships an opt-in Watchtower override. It only touches containers carrying the Minutd label and checks daily:
docker compose -f compose.yaml -f compose.watchtower.yaml up -d
It stays inside your MINUTD_TAG line, so with 0.1 it applies patch releases only. It does not take backups. Think about that before enabling it on an install you rely on.
Update notice
Off by default. In Settings, Updates, you can switch on a notice that tells you when a new version is released. When it is on:
- The Minutd server (never your browser) sends one request at most every 24 hours:
GET https://api.github.com/repos/dennisklappe/minutd/releases/latest. - The request has no body, no cookies, no token and no identifiers. Its only headers are
Accept: application/vnd.github+jsonandUser-Agent: minutd-update-check. GitHub sees your server’s IP address, as with any request. - If the newest release is newer than yours, a dismissible banner appears. Dismissing hides that version only.
- A failed check is not retried until the next day and never blocks anything.
Switched off, Minutd makes no such request. Nothing else in Minutd contacts a Minutd server, and there is no telemetry.
Version
The running version is shown at the bottom of every page and at /api/version.