Upgrades
XERJ follows SemVer. Patch releases (0.1.x) are drop-in replacements. Minor releases (0.x.0) can introduce backward-compatible schema fields. Major releases (x.0.0) may change the on-disk format — when they do, the binary ships a one-shot migration tool.
Single-node upgrade
# 1. Back up (see Backup & restore)
$ curl -sXPOST -H "Authorization: ApiKey $XERJ_API_KEY" \
http://127.0.0.1:8080/v1/admin/backup \
-d '{"destination":"s3://my-backups/xerj/pre-upgrade"}'
# 2. Fetch and verify the new release — extraction is chained to the checksum,
# so an archive that fails to verify is never unpacked over the running node
$ (
ver=1.0.0-rc.18 # the release you are upgrading to
target=x86_64-unknown-linux-musl # your platform's target triple
stage="xerj-${ver}-${target}"
asset="${stage}.tar.gz"
base="https://github.com/xerj-org/xerj/releases/download/v${ver}"
curl -fsSLO "$base/$asset"
curl -fsSLO "$base/$asset.sha256"
want=$( { sha256sum "$asset" 2>/dev/null || shasum -a 256 "$asset"; } | cut -d ' ' -f 1 )
printf %s "$want" | LC_ALL=C grep -qE '^[0-9a-f]{64}$' \
|| { echo "no working SHA-256 tool — refusing to install an unverified $asset" >&2; exit 1; }
tr -d '\r' < "$asset.sha256" \
| LC_ALL=C grep -qxF -e "$want $asset" -e "$want *$asset" \
|| { echo "CHECKSUM MISMATCH for $asset — do not install it" >&2; exit 1; }
tar xzf "$asset"
)
# 3. Replace the binary in place (reached only when step 2 verified and unpacked)
$ sudo systemctl stop xerj
$ sudo install -m 0755 "xerj-1.0.0-rc.18-x86_64-unknown-linux-musl/xerj" /usr/local/bin/xerj
$ sudo systemctl start xerj
# 4. Confirm the node reports the version you just installed
$ curl -s http://127.0.0.1:8080/v1/health | jq .version
"1.0.0-rc.18"
Downtime is the time it takes the WAL to replay — usually a few seconds for workloads in the low millions of live docs.
Cluster upgrade
The cluster wire protocol has no version negotiation: a node on a new wire version cannot talk to a node on the old one, in either direction. Upgrading a cluster is a full stop/start of all nodes in one maintenance window, not a rolling restart — there is no drain/activate API and no shard migration to sequence around.
# On every node, within the same window: $ ssh node-<id> sudo systemctl stop xerj $ ssh node-<id> sudo install -m 0755 xerj-new /usr/local/bin/xerj $ ssh node-<id> sudo systemctl start xerj # The ring re-elects a leader on its own once a quorum is back up
Because index data is node-local today, each node's data directory is also each node's upgrade responsibility — snapshot per the backup guide before touching the binary.
On-disk format
There is no migration step and no migrate subcommand: a newer binary opens an older data directory in place. What guards the layout is a format marker that every index directory carries, <data_dir>/<index>/xerj_meta.json:
$ sudo cat /var/lib/xerj/logs/xerj_meta.json
{"format_version":1,"xerj_version":"1.0.0-rc.95"}
A binary checks the marker before it opens anything else in that index. If format_version is higher than it understands, it does not open the index, so it cannot misread or garbage-collect segments it does not recognise. The node still starts; the index is logged as failed to open, with this reason:
data dir /var/lib/xerj/logs was written by a newer xerj (data-dir format version 2); this binary supports up to 1. Refusing to open — upgrade xerj to a build that understands format 2.
Current binaries read format 2. Opening an index never raises its marker. It moves from 1 to 2 only when a flush writes a full-text field whose name cannot be used literally in a file name: longer than 128 bytes, or containing a character such as / \ : * ? " < > |.
Rolling back
Roll back by reinstalling the previous binary. That works for every index whose marker is still at a version the previous binary reads; check the xerj_meta.json files first. An index whose marker has moved past it will not open under the old binary, and rolling it back means restoring the backup you took before the upgrade.
Source · engine/crates/xerj-server/src/main.rs · engine/crates/xerj-storage/src/index_store.rs (check_data_dir_version) · engine/crates/xerj-fts/src/index.rs (field_file_component)