Upgrading an instance
One version covers the whole project — the server, the admin UI and the website — so an upgrade moves all of them together. How it is done depends on how the instance was installed; the rule is the same everywhere: back up, switch to the new version, restart, check.
Important
The server applies the database migrations of the new version when it starts. After that the
previous version is not expected to work against the database: the only supported way back is
the backup taken before the upgrade (operations.md).
Update notice
When a newer stable release exists, the admin UI says so: a notice at the top of every admin page names the available version and the running one, and the version in the footer carries the same mark. That is all it does — the instance never updates itself. An upgrade runs database migrations and binds the database to the new version, so it stays a deliberate step of the operator, with a backup taken first — the rest of this page.
How it works:
- Where the version comes from. The server asks GitHub for the latest release of the project
(
api.github.com/repos/Fristail27/vocab-bloom-hub/releases/latest). GitHub's "latest" skips drafts and prereleases, and versions are compared as semantic versions:1.10.0is newer than1.9.0, and an instance on1.1.0-beta.1is not offered1.0.0. - How often. A few times a day: the answer is kept for six hours, a failure for half an hour. No page view triggers a request of its own. When GitHub does not answer — no network, a rate limit — the result is "unknown" and nothing is shown; the server logs one warning per period.
- Who sees it. The signed-in admin only (
GET /api/settings/update-checkis behind the admin guard). Visitors of the website and of/api/v1learn nothing about the version's age. - What is sent. One anonymous
GETwith aUser-Agentofvocab-bloom-hub/<version>. Nothing about the instance or its data; GitHub sees the address the request comes from, as with any request. - Turning it off.
UPDATE_CHECK=false(environment.md): no update-check requests or notices. This does not disable explicit installation downloads or the first-start dictionary import. The startup log names the setting. - The datasets have a notice of their own. The same switch covers the check for a newer file
of a source — Wiktionary, the Open English WordNet — shown on the card of the dataset
(
datasets.md).
Closing the notice hides it for that release in this browser; the next release shows it again. The two links of the notice lead to the release notes on GitHub and to this page.
Before you upgrade
-
Read the release notes — the changelog entry of every version between yours and the new one. It names the migrations a release ships and the settings that changed (
v1.0.0, for one, moved the default ports of docker compose). -
Know what you run. The admin footer shows the version; so does
GET /api/health({"status":"ok","version":"1.0.0"}). -
Back up the database. For the bundled Postgres of docker compose:
docker compose exec -T postgres pg_dump -U vocab vocab_bloom | gzip > backup-$(date +%F).sql.gzvocab/vocab_bloomarePOSTGRES_USER/POSTGRES_DBof.env. Any other database, and restoring:database.md.
Docker: the published images
The installation of the quick start: images pulled from
GHCR, the version picked by VBH_TAG in .env.
# .env: VBH_TAG=1.2.0
docker compose pull
docker compose up -d
up -d recreates only the services whose image changed; the database and its volume are not
touched. What VBH_TAG may hold:
VBH_TAG | What an upgrade is |
|---|---|
1.2.0 | Edit the line, then pull and up -d — nothing moves until you say so |
1.1 or 1 | pull and up -d fetch the newest patch (or minor) release of that line |
latest | The newest stable release, whatever its number — for trying things out |
main | The development build of every merge; may carry migrations of unreleased code |
Note
With the observability overlay, or any second compose file, pass the same -f files to pull
and up -d as at the first start — or name them once in .env:
COMPOSE_FILE=docker-compose.yml:docker-compose.observability.yml
(deployment/docker.md).
Docker: built from a checkout
An instance built on the server with docker-compose.build.yml — the way to run the website
under a hostname of your own (deployment/docker.md).
git pull --ff-only # or: git fetch --tags && git checkout v1.2.0
docker compose -f docker-compose.yml -f docker-compose.build.yml build
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d
docker image prune -f # the images of the previous build
The old containers keep serving while the new images build; the switch itself takes seconds.
A change to the documentation only rebuilds the website: build site, then up -d site. The
same steps with the backup and the checks around them, as an update.sh:
deployment/vps.md.
Without Docker
git pull --ff-only # or a release tag
yarn install --immutable
yarn build # the server and the admin UI
yarn site:build # the website, when you run it
# restart the processes: systemctl restart …, pm2 reload …
The admin UI and the website inline their NEXT_PUBLIC_* values when they are built, so the
rebuild is part of every upgrade. Process managers, probes and a restart without dropped
requests: deployment/README.md.
After the upgrade
curl -s localhost:3240/api/ready # {"status":"ok"}: the migrations ran, the database answers
curl -s localhost:3240/api/health # "version" is the new one
(3010 instead of 3240 without Docker.) A 503 on /api/ready that does not go away means
the server could not migrate or reach the database — docker compose logs server says which.
The update notice disappears by itself: it compares the running version on every request.
Rolling back
Restore the backup taken before the upgrade and start the previous version — VBH_TAG back to
the old number, or git checkout of the old tag and a rebuild. A release without migrations
rolls back by starting the previous build alone. Why a plain downgrade is not enough, and what
a failed migration leaves behind: operations.md.
Upgrading to a version with datasets
The first version that keeps several datasets (datasets.md) migrates the
database by itself, like any other, and changes nothing a reader can see:
- the dictionary tables stay where they are, in
public, and become the datasetdefault— the active one. Nothing is copied, the migration takes a moment whatever the size; - the registry (
datasets) is created with that one row: the project's dataset, CC BY 4.0, the version the instance had recorded; - the public API gains fields and loses none:
sourceon a word,dataset,sourceandattribution_urlin/api/v1/meta. The SDKs of 1.0 read the answers of the new server, the new SDKs read the answers of a 1.0 server.
Rolling back is the usual restore of the backup taken before the upgrade. An instance that already holds other datasets cannot be served by a version that does not know them: its tables are in schemas the old code never looks into.
Upgrading to a version with the history of edits
The first version that keeps a history of edits
(datasets.md) migrates every dataset
by itself:
- the table
en_changesis created in the schema of every dataset, empty: a dataset is taken to be what its source published, and the history holds what is changed from here on. The entries edited before keep theiruser_modifiedflag — they are kept through an update as they were — and are not shown as modified; - the public API gains fields and a route, and loses none:
modifiedon a word and on every part of one,license_textandmodified_entriesin/api/v1/meta,GET /api/v1/words/{word}/history,author_nameandauthor_consentof a suggestion. The SDKs of earlier versions read the answers of the new server; - the edits of the dictionary leave the audit journal: from this version on they are
written to the history of the dataset only. The rows the journal already holds stay on the
Events tab of the History page until
AUDIT_RETENTION_DAYSremoves them; a tool that read word edits fromGET /api/en/auditreads them fromGET /api/en/changesnow; - human-authored public datasets (Wiktionary and the WordNets) refuse entries marked as
generated (
400 generated_not_allowed); OpenGloss declares and permits generated content.
Upgrading to a version that reads every dataset
The version that reads a headword from every dataset at once
(datasets.md) needs no migration:
- the public API gains two routes and loses none:
GET /api/v1/words/{word}/datasetsandGET /api/v1/words/{word}/datasets/{dataset}/history. Every other route answers what it answered, from the active dataset; - an instance that holds several datasets opens up to four more database connections per
dataset that is not the active one, once such a read arrives — count them against the
connection limit of a managed Postgres, next to
DB_POOL_SIZE; - the settings gain a field the server writes,
dataset_removed_at: when a dataset was last deleted. It dates the answers of the new routes and is not meant to be edited.
Upgrading to a version that reads the versions from the files
The version that records a dataset of a public source by what its file says
(datasets.md) needs no migration:
- a dataset that is installed keeps the version it has — the day it was installed. Install it again from the file of its source, the one you have or a newer one, to record the version of the file; until then the card of a WordNet dataset says that its edition is not known and gives no notice of a newer one, and the one of Wiktionary counts its 30 days from the day of the installation;
- with
UPDATE_CHECKon, an instance that holds Wiktionary or the Open English WordNet asks kaikki.org and the GitHub API once a day. An instance that must make no outgoing request hasUPDATE_CHECK=falsealready, and asks nothing; dataset_versionof/api/v1/metachanges for such a dataset at its next installation, from a day to a day or an edition. It was never promised a format;- the import of the project's dataset offers the update of the published dataset only. It used to offer it for a dataset of another source too, and the server refused.
Upgrading to a version with datasets of your own (1.1.0)
The version that lets the admin create datasets of their own and edit any dataset without
serving it (datasets.md) migrates the database
by itself, like any other:
- the registry of datasets gains
title,license_textandown(two shared migrations,AddOwnDatasetsandAddOwnDatasetMark); every dataset of the catalog is registered as the catalog's, nothing else changes in the data; - the public API gains fields and loses none:
titlein/api/v1/metaand in the groups ofGET /api/v1/words/{word}/datasets. The SDKs of 1.0 read the answers of the new server; - the admin routes of the dictionary take an optional
?dataset=; without it they work on the active dataset, as before. The admin UI has the switch of the dataset that is edited in its header; en_dataset_versionin the settings is read-only from here on: the version of a dataset is what its file said, written by an import — the settings field mirrors it. An instance whoseen_dataset_versionwas typed over by hand reports the version the registry holds;- the import and the export are actions of the card of a dataset on the datasets page; the
addresses
managing/import-dictionaryandmanaging/export-dictionaryredirect there.
Sources, licenses, forks and OpenGloss (1.2.0)
Back up the database and upgrade the server, admin UI and website together. The source-tracking
feature adds shared migrations AddDatasetOrigins and AddDatasetTermsUpdatedAt, and
per-dataset migrations AddWordOrigins, NormalizeWordOrigins and AddChangeContribution.
They run automatically on PostgreSQL. Existing entries retain known dataset terms; unknown
earlier sources, versions and contributions are not invented. A terms_updated_at column
error means the shared migrations have not completed; use the
migration troubleshooting guide.
Own datasets and forks can now set and edit their version. Original source terms and the terms of actual edits are retained separately; changing dataset settings does not rewrite old snapshots. Forks are independent copies, with no automatic parent synchronization.
OpenGloss 2.4 and server downloads use this infrastructure and need no additional migration.
An upgrade adds the catalog option without downloading or installing the dictionary.
Start installation from its card; all six Parquet files are fetched from pinned catalog links,
with manual upload as a fallback. No new environment variable or token is required.
UPDATE_CHECK does not check for newer OpenGloss releases.
Current exports retain ordinary filenames but add a format marker and structured source metadata. Upgrade the receiving instance before importing them; do not remove metadata to make an old importer accept the files. See export compatibility.
The dictionary is updated separately
A new version of the code does not change the dictionary data, and a new dataset revision does
not need a new version of the code. The import of the project's dataset in the admin UI says when a newer dataset
exists; what an update replaces and what it keeps:
operations.md.