Upgrading MFConsole¶
How to move to a newer version, back up first, and roll back if something goes wrong. The exact steps differ by deployment type — check which one you're running before you start.
Before you upgrade: take a backup¶
If Cloud Backup (S3) is configured (Settings → Cloud Backup), the console already backs up its own database and configuration every night. Don't rely on one that might be up to a day stale for an upgrade you're about to do on purpose — trigger a fresh one first from Settings, and confirm it shows verified before continuing.
If Cloud Backup isn't configured yet, set it up before upgrading — see the Backup & Restore how-to. An upgrade without a backup in place has no safety net if it goes wrong.
Schema changes only ever add
MFConsole's database migrations only add columns and tables, never remove or rename them. That's what makes rolling back safe by default: older code simply ignores columns it doesn't know about, so you don't need to restore the database just to undo a code upgrade — only if the upgrade itself somehow behaved badly and you also want to discard data it wrote in between.
Single-host deployments¶
The default (Free-edition) install runs one console host with no HA components. This is the common case, and it's the simplest to upgrade.
Option A — the in-console "Update Now" button (Settings → System →
Updates). This pulls the new image and restarts app/worker/beat
automatically — you'll see a brief interruption while they restart, and
the page reloads once it's back. This only works on a registry-pull
install (the default); it's not available if you built the image from
source yourself.
Option B — re-run the installer from the directory you installed into:
Never re-extract into a new directory to update
Re-running the "deploy from scratch" steps in a second directory
reuses the same database volume (Compose names it after the directory,
always mfconsole) while generating a new database password —
Postgres only applies a password at volume creation, so the app
crash-loops on password authentication failed forever. Always cd
into your existing install directory and re-run install.sh there.
Update Now needs Docker
The button works through the watchtower container, which talks to the
Docker socket. A Podman host has no /var/run/docker.sock unless the
podman-docker package and podman.socket are set up, so there
watchtower restarts over and over and Update Now does nothing. Use
Option B on Podman.
Rolling back a single-host deployment¶
Roll back the same way you upgrade: re-run the installer from your existing install directory with the version you want to go back to.
It keeps your .env secrets, pulls that version and recreates the
containers. It also writes the version into .env as MFCONSOLE_IMAGE,
which is what makes the rollback stick.
Don't roll back with a one-off export
export MFCONSOLE_IMAGE=… followed by compose up -d does roll back,
but only for that shell. .env still names the newer version, so the
next compose up from anywhere else (a reboot script, someone
recreating a container) quietly moves you forward again. If you have
already rolled back that way, run ./install.sh <previous-version> as
above, or set MFCONSOLE_IMAGE in .env by hand.
Check that it took: the version badge in the console's top bar, and
grep ^MFCONSOLE_IMAGE= .env. Per the note above, this is safe without
touching the database unless the version you're rolling back from did
something non-additive to the schema. Check that release's changelog entry
if you're unsure.
Measured on a single host (4 vCPU, 8 GB, Podman, 2026-10-04): an upgrade or a rollback takes about 80 seconds until app, worker and beat are healthy again. The console answers for all but about 15 to 20 seconds of that. Going back from 1.3.0-rc5.5 to 1.2.0-rc5.1, across 7 new tables and 72 new columns, worked with no database changes, and every account, MFA enrollment and setting was still there.
HA (3-node) deployments¶
There is no automated "Update Now" for an HA cluster yet — each host's
app-N/worker/beat needs pulling and recreating individually,
leaving the host currently holding the VIP for last so the console
only takes one brief interruption instead of two:
# on each non-VIP-holding host first, then the VIP holder last:
export MFCONSOLE_IMAGE=docker.io/mfconsole/kvm-manager:<new-version>
docker compose -f docker-compose.yml -f compose.prod.yml --profile ha pull
docker compose -f docker-compose.yml -f compose.prod.yml --profile ha up -d --no-deps --force-recreate app-N worker beat
(substitute app-1/app-2/app-3 for the host you're on). Verify each
host is healthy before moving to the next:
docker compose ps # every service Up/healthy
curl -sk https://127.0.0.1/ -o /dev/null -w '%{http_code}\n' # 200
Rolling back an HA deployment follows the same per-host procedure with the
previous version's MFCONSOLE_IMAGE.
Compute nodes after an upgrade¶
Some fixes only reach already-enrolled hypervisor nodes after a Repair Worker pass (Hosts → a node → Repair Worker) — the console doesn't yet detect on its own whether a node needs one. Check that release's changelog entry for an explicit call-out, and when in doubt, running Repair Worker after any upgrade is harmless even if it wasn't strictly needed.