An on-premises deployment has two things to keep current: the platform and the collectors in your network. Versus is one image across every form factor, so upgrades are consistent — pull a newer image, let the forward-only migrations run, roll the app tier. Collectors keep themselves current. Here's how to do it safely on each install type.
Web/app tier + database. Upgrade by pulling a newer image (or OVA); forward-only migrations run before it serves. Rolls with little/no downtime on Helm and clusters.
Run the bundled supervisor and they self-upgrade — signed, health-checked, with automatic rollback. Currency across the fleet with no manual effort.
FIELD_ENCRYPTION_KEYbefore every upgrade — the key is what makes encrypted columns readable; losing it is unrecoverable. (2) Test the upgrade in staging first for anything beyond a patch.GET /api/updates/health/(also the load-balancer/liveness check). Each install writes a version.json at startup..veslic has its own validity + daily heartbeat. See On-Premise Licensing.Supported-version policy. The two most recent minor releases receive fixes and security updates; upgrade one minor at a time so migrations apply in order. Security releases ship on their own and are called out explicitly in the release notes with the CVEs they address — apply them promptly. Keep collectors on auto-update so the fleet tracks the platform.
Versus runs a documented vulnerability-management program. Remediation targets for issues in the software — subject to upstream patch availability and risk assessment — are:
| Severity | Remediation target |
|---|---|
| Critical | 7 days |
| High | 30 days |
| Medium | 90 days |
| Low | 180 days |
Behind every build:
Versus uses MAJOR.MINOR.PATCH version numbers (the current line is 2.x). The running version is shown in System → Settings and returned by GET /api/updates/version({ version, build_time, git_hash }) and the GET /api/updates/health/ check.
| Release | Example | What it means |
|---|---|---|
| Major | 2.x → 3.0 | Significant change; may carry non-trivial migrations or changed defaults. Read the upgrade notes and test in staging. |
| Minor | 2.3 → 2.4 | New features, backward-compatible. Forward-only migrations run automatically on startup. Test in staging. |
| Maintenance / patch | 2.4.0 → 2.4.1 | Bug fixes and security fixes — no feature or schema surprises. Safe to apply promptly. |
Security fixes ship as maintenance releases and are called out explicitly with the CVEs they address — see Security releases & vulnerability remediation above. A minor release stays supported — fixes and security patches — until the second newer minorships; a line's end-of-support is flagged in the release notes ahead of time so you're never surprised.
Knowing a release is out. Compare your running version (above) against the latest on Support → Downloads, where each build is published with its release notes, SHA-256 checksum and detached signature. Security releases are flagged so you know to apply them promptly — ask your Versus contact to add you to the security-advisory notificationslist so those reach you directly. There is no auto-updater for the platform: upgrades are a deliberate, operator-run step. Collectors, by contrast, keep themselves current — see below.
Getting the build. On-premise images live under Support → Downloads → On-Premise Installers — a VMware ESXi OVA and a KVM qcow2, each entitlement-gated and shipped with a SHA-256 checksum and signature (Kubernetes / Helm packaging follows the appliance). Always verify the checksum and signature before loading.
pg_dump/snapshot) — your rollback path across a migration.FIELD_ENCRYPTION_KEY (and the appliance DB password) are escrowed somewhere safe and separate.A worked example — a single-node appliance moving to the next release. Every form factor has the same shape; only the apply command differs (see the per-install-type sections below).
pg_dump the database and confirm your FIELD_ENCRYPTION_KEYis escrowed separately — that pair is your way back across a migration.docker load the tarball; verify the SHA-256 and signature first. ./versus-upgrade.sh on the host — it recreates the stack (forward-only migrations run on startup), health-checks the new version, and rolls back if it doesn't come up. (By hand: docker compose up -d, then watch docker compose logs -f web.)GET /api/updates/health/ is OK, System → Settings shows the new version, licensing reads Active, and a known workflow or path check succeeds.Kubernetes, clustered appliances, and air-gapped sites follow the same seven steps with the commands in the sections below.
The appliance runs the stack (Caddy + web + bundled Postgres) via Docker Compose in the VM. Upgrade it two equivalent ways:
versus-upgrade.sh --serve) carries it out.cd deploy/appliance
./versus-upgrade.sh # upgrade to the latest signed release
./versus-upgrade.sh 2.4.2 # or pin a specific versionEither way it pulls the signed image, recreates the stack (forward-only migrations run on web startup), health-checks the new version, and rolls back automatically if it doesn't come up. To drive it by hand instead: docker compose pull && docker compose up -d (watch docker compose logs -f web); on an air-gapped host docker load the tarball first and the script continues offline.
A run is a transparent, timestamped log — each phase, then the outcome on the last line:
$ ./versus-upgrade.sh 2.1.0
2026-10-25T16:40:02Z [versus-upgrade] target: version=2.1.0 image=versus/web-onprem:2.1.0
2026-10-25T16:40:02Z [versus-upgrade] upgrading to 2.1.0 (image versus/web-onprem:2.1.0); previous=versus/web-onprem:2.0.0
2026-10-25T16:40:02Z [versus-upgrade] [downloading] pulling versus/web-onprem:2.1.0
[+] Pulling 2/2
✔ web Pulled
✔ gateway Pulled
2026-10-25T16:40:48Z [versus-upgrade] [migrating] docker compose up -d — forward-only migrations run on web startup
[+] Running 4/4
✔ Container versus-postgres-1 Healthy
✔ Container versus-web-1 Started
✔ Container versus-gateway-1 Started
✔ Container versus-caddy-1 Running
2026-10-25T16:41:05Z [versus-upgrade] [verifying] waiting up to 240s for 2.1.0 to report healthy
2026-10-25T16:41:35Z [versus-upgrade] [succeeded] upgrade to 2.1.0 complete
2026-10-25T16:41:35Z [versus-upgrade] DONE: upgraded to 2.1.0If the new build doesn't pass its health check, the script reverts the image on its own and says so — no manual recovery, no half-upgraded state:
2026-10-25T18:04:06Z [versus-upgrade] [verifying] waiting up to 240s for 2.1.1 to report healthy
2026-10-25T18:08:06Z [versus-upgrade] ROLLING BACK to versus/web-onprem:2.1.0 (new build did not report healthy within 240s)
2026-10-25T18:08:19Z [versus-upgrade] [rolled_back] rolled back to previous image
2026-10-25T18:08:19Z [versus-upgrade] NOT upgraded (rolled back or failed) — see above docker compose up -d — restore the database from backup only if a migration ran that the older build can't read (hence the snapshot).You only need a fresh OVA/qcow2 to pick up base-OS patches — routine app upgrades just pull the image.
helm upgrade versus deploy/helm/versus --reuse-values --set image.tag=<new>helm rollback versus <REVISION>; restore the external DB from backup too if the migration wasn't backward-compatible. helm upgrade. See On-Premise Installationfor the OpenShift specifics (Routes, restricted-v2 SCC, arbitrary-UID image).Multiple app nodes share one external database.
migrate), with RUN_SEEDS=0 on the others. /api/updates/health/ OK, next. No full outage.No install needs outbound internet. To upgrade an air-gapped site:
docker load the shipped tarball (or mirror it to your internal registry; on OpenShift oc image mirror). On appliance / Compose, then run ./versus-upgrade.sh <version>: it sees the image is already present and continues without reaching a registry. On Helm, point image.tag at the mirrored image. .veslic under System → Licensing.Migrations are forward-only and run automatically before the app serves traffic. They're the reason the pre-upgrade database backup matters — it's the supported way back across a schema change.
Collectors keep themselves current — the main lifecycle win. You run the bundled supervisor, which performs signed, health-checked self-upgrades with automatic rollback. See Keeping the Collector Running and the Collector Control Plane.
-manifest-url <URL> -update-pubkey <HEX>(both from Client Tools). On a new signed build it stages, launches, and health-checks it; if the new version can't establish a healthy session it rolls back. A collector never gets stuck on a bad build.System → Settings → Software Updates shows the version you're running and whether a newer release is available on your channel — read from a signed release manifest, so the check itself is tamper-evident. Security releases are flagged distinctly. From the card you open the release notes and, depending on form factor, either apply the upgrade in one click or copy the exact command:
versus-upgrade.sh service, which pulls the signed image, lets the migrations run, health-checks the new build, and rolls back automatically if it doesn't come up. Prefer the terminal? The same script runs standalone: ./versus-upgrade.sh.helm upgrade (or node-roll) command. These upgrades stay declarative — Versus never mutates your cluster from inside itself.

The check is read-only and needs no outbound telemetry beyond fetching the manifest; an air-gapped site points it at an internal mirror (or just uses the procedures above). There is deliberately no silent auto-updater — upgrading the platform is always something you choose.
Upgrades are built to fail safe, in three layers — and the same machinery that keeps a running deployment healthy backs the last two:
See Self-Healing: Detected Issues & Support Bundles for how detection, redaction, signing, and transmission work.
GET /api/updates/health/ → OK once the DB is reachable + migrated.| Form factor | App-tier rollback | If a migration ran |
|---|---|---|
| Appliance (VM) | Restore VM snapshot, or revert image tag + up -d | Restore DB from pre-upgrade backup |
| Docker Compose | Set prior image tag, docker compose up -d | Restore DB from pre-upgrade backup |
| Kubernetes / Helm / OpenShift | helm rollback versus <REV> | Restore external DB from backup |
| Clustered appliances | Roll nodes back to prior image | Restore shared DB from backup |
| Collector | Supervisor auto-rolls back a bad upgrade; or drop in the prior binary | n/a |
The consistent thread: the app tier rolls back in seconds; the database rolls back from the backup you took first. Take the backup.