Updating
An update is a pinned image digest plus the migrations bundled with it. One script does the whole sequence, and the same script runs whether we host you or you host yourself.
./scripts/plugboard-update.sh 0.2.0What the script does, and why each step is there
Section titled “What the script does, and why each step is there”| Step | Why |
|---|---|
| 1. Pre-flight | Refuses to start from an unhealthy stack. An update is not a repair tool, and starting from a broken state makes the failure impossible to attribute |
| 2. Backup | A dump, size-checked and integrity-checked before anything changes. An empty or truncated dump is worse than no dump, because it looks like a backup |
| 3. Pull | By digest where possible. Verifies the cosign signature if cosign is installed |
| 4. Migrate | Forward-only and idempotent, so re-running is safe |
| 5. Swap | Recreates the application services on the new image |
| 6. Health gate | /health/ready must pass and the instance must report the version you asked for, within 120 seconds |
| 7. Rollback | On any failure after step 4, the previous image comes back automatically |
The version check in step 6 exists because a tag that silently resolved to the old image passes every other check and looks like a successful update.
The database is not rolled back automatically
Section titled “The database is not rolled back automatically”Migrations are forward-only. An automatic restore would discard everything written between the backup and the failure, so if a migration is implicated the script tells you which dump to restore and stops.
That is a deliberate choice. Losing ten minutes of tickets to an automatic fix is worse than a pause.
Before you start
Section titled “Before you start”-
Read the [changelog](https://github.com/samiossoftware/plugboard/blob/main/C HANGELOG.md). Releases marked with a migration warning change the schema. Treat those with more care and prefer a quiet window.
-
Confirm
SECRETS_MASTER_KEYis backed up somewhere other than the server. A database restored without it cannot decrypt its own secrets. -
Rehearse if you want to.
--dry-runchecks the stack and pulls the image without changing anything:Terminal window ./scripts/plugboard-update.sh 0.2.0 --dry-run
Pinning a digest
Section titled “Pinning a digest”Tags can be moved by whoever controls the registry. A digest cannot. For production, pin the digest published in the release notes:
./scripts/plugboard-update.sh sha256:1a2b3c...The script writes the pin itself, and it writes both halves. Compose builds its
reference as ${PLUGBOARD_IMAGE}:${PLUGBOARD_VERSION}, so a digest is spelled by
moving the marker into the image name and leaving the bare hex as the version:
PLUGBOARD_IMAGE=ghcr.io/samiossoftware/plugboard@sha256PLUGBOARD_VERSION=1a2b3c...A tag pin is the ordinary shape, and the script restores it on rollback:
PLUGBOARD_IMAGE=ghcr.io/samiossoftware/plugboardPLUGBOARD_VERSION=0.2.0Verifying a release
Section titled “Verifying a release”Every release is signed, and each one publishes a list of the software it contains, which is what a supply-chain questionnaire is asking for.
The update script checks the signature for you when the verification tool is present, so most sites never do this by hand. If your policy requires you to, or you want to check a download before an update window:
cosign verify ghcr.io/samiossoftware/plugboard@sha256:1a2b3c... \ --certificate-identity-regexp '^https://github.com/samiossoftware/plugboard/' \ --certificate-oidc-issuer https://token.actions.githubusercontent.comcosign download attestation ghcr.io/samiossoftware/plugboard@sha256:1a2b3c...Portable bundles are covered by a published checksum file instead. The automatic updater checks it and refuses an archive that does not match, which matters because it runs unattended with privileges.
Rolling back
Section titled “Rolling back”./scripts/plugboard-update.sh 0.1.0The script prints this command with the right version at the end of every successful run, so the way back is always in your scrollback.
If a migration is implicated, restore the dump the script took in step 2 before rolling the image back. Restoring an older schema under a newer image will not work, and the script says so rather than guessing.
What version am I on
Section titled “What version am I on”curl -s https://helpdesk.yourschool.org/api/health/version{ "version": "0.1.0", "channel": "stable", "commit": "9f2c1ab44e01", "builtAt": "2026-08-01T09:14:22Z" }The same information is on /api/health, and exposed to Prometheus as
plugboard_build_info. These endpoints deliberately need no tenant and no
database, so they still answer when the instance is unhealthy.
Automatic updates, on-premises
Section titled “Automatic updates, on-premises”The on-premises bundle updates itself by default. Every night at 01:00 in the machine’s own local time it asks whether a newer release exists, downloads it, checks it against its published checksum, and stages it. The swap happens on restart, because replacing files a running process holds open is how updaters corrupt themselves, especially on Windows.
Nothing is applied during the day. An update restarts the service, and doing that while the service desk is in use is the surprise the schedule exists to avoid.
./plugboard update --check # is there one? change nothing./plugboard update # get it now, do not wait for tonightTune or turn it off in .env:
AUTO_UPDATE=off # never update on its ownAUTO_UPDATE_HOUR=3 # a different local hour, 0-23AUTO_UPDATE_RESTART=off # stage it, but apply at your next restartAn archive whose checksum does not match the release is never unpacked. This runs unattended with privileges, so “probably the right file” is not good enough.
The previous version is kept in .previous-version, so a bad update can be
undone without another download.
Updating a source install
Section titled “Updating a source install”git fetch --tagsgit checkout v0.2.0pnpm installnode scripts/plugboard.mjs migrateThen restart the service. Take a database dump first. Migrations are forward-only and there is no automatic way back.
Release channels
Section titled “Release channels”RELEASE_CHANNEL in .env is stable or beta. Beta deployments get releases
one ring earlier and help catch problems before a release reaches everyone.
It changes which release we recommend to you. It never causes an automatic update on the Docker or source routes.
Nothing updates itself there. Every update is a decision someone makes, on a deployment holding real data.
If you are on managed hosting
Section titled “If you are on managed hosting”We run the same script on the same sequence, after the release has soaked on our own instance and the public demo. You are told before a migration-bearing release, and you can ask to be moved to a later ring. See releases and update windows.