Updates And Backups

Back up before every update, keep your settings in the private config, and keep your code changes in your own repository. Migrations only move forward.

Versions

health.php and Control Panel > Diagnostics report several separate versions. Never treat one as another:

NameCurrentSet in
Server release0.1.0lib/version.php (SERVER_VERSION)
Client release0.1.0ExampleMod CMakeLists.txt (project(... VERSION))
Protocol1Both PROTOCOL.md files and both sides' code
Baselinebaseline-1A template feature-set label, not a release
Schema007_connector_callssql/ files and SCHEMA_MIGRATIONS in lib/app.php

Bump the protocol only for a change a version-1 peer would misread, and then in both repositories together.

Backups Versus Checkpoints

KindContainsWhere
Full backup: sudo bash scripts/backup.shThe whole database and config/config.php/home/dwemer/example-ai-server-backups/<time>/, outside the web root
Dashboard backup: Control Panel > BackupsThe database only, no configbackup_dir from the config, or a temporary folder that may be emptied on restart
Session checkpoint: Roleplay > CheckpointsOne session's conversation lines and memory notesInside the same database

A checkpoint is not a backup: it is lost with the database. Backup folders contain your token and keys; keep them private and out of Git. Checkpoints are covered in Data and Memory.

Update A Manual Install

In WSL:

cd /var/www/html/ExampleServer
git rev-parse HEAD
sudo bash scripts/backup.sh
sudo bash scripts/update.sh

Write down the commit that git rev-parse HEAD prints and the backup folder. Together they are the matching code and data to return to.

  • update.sh refuses tracked local edits, backs up the database and config, fast-forwards only, then migrates.
  • It stops if your branch has no upstream or has diverged; merge by hand.
  • It stops if the update would start tracking a file you have privately, such as config/config.php.
  • It never resets, cleans or deletes anything, and never restores anything itself.

A manual install follows the branch it was cloned from, main or dev. A launcher install is updated with the launcher's Update and Switch commands; see the launcher route.

DwemerDistro updates and the custom mods port. Update Distro keeps your CUSTOM_MODS_PORT and other service ports, so your custom mod addresses stay the same. If you change the port, run sudo ddistro_custom_mod setup-web and pair every client again. See Ports and branches.

Migrations

  • scripts/migrate.php (run by install.sh and update.sh) applies missing migrations in order, one transaction per file. A second run applies nothing.
  • If one fails, that file is rolled back, earlier ones stay and the code is already updated. update.sh exits 3 and prints the backup folder. Prefer fixing forward: fix the cause and run sudo -u dwemer php scripts/migrate.php again.
  • There are no down migrations. To go back to before the update, return code and data together:
    1. Stop the client.
    2. Check out the commit you recorded without resetting or cleaning, for example git switch --detach <commit>. This keeps config/config.php, backups and other untracked files. Commit or copy your own local edits first.
    3. Restore the database from the backup made with that update, as in Restore a backup, and the config from it if needed.
    Code and database must match: newer code on the older database answers 503 not_migrated for the missing migrations, and older code on the newer database answers 503 schema_too_new.
  • A database with migrations the code does not know answers 503 schema_too_new. Update the code; never downgrade a database by hand.

To add one, create a new sql/NNN_name.sql and append its name to SCHEMA_MIGRATIONS. Never edit an applied file.

Restore A Backup

To inspect a backup without touching the live database, restore it into a disposable one and compare:

sudo -u postgres createdb --owner=dwemer --template=template0 example_ai_restore_check
sudo -u postgres pg_restore --no-owner --role=dwemer -d example_ai_restore_check < /path/to/backup/YOUR_DATABASE.dump
sudo -u postgres psql -d example_ai_restore_check -c 'SELECT count(*) FROM ex_turns'
sudo -u postgres dropdb example_ai_restore_check

The dashboard's Verify restore does a similar check into a new <database>_verify_<random> database, so the configured database role needs CREATEDB; remove it yourself with dropdb when finished. To really restore, stop the client first, then run this. It overwrites that database:

sudo -u postgres pg_restore --clean --if-exists -d YOUR_DATABASE < /path/to/backup/YOUR_DATABASE.dump

Use this copy's configured database name (default example_ai_mod). Restore the config file from a full backup separately if needed. Run sudo bash scripts/install.sh afterwards to repair its permissions only when the checked-out code matches that backup, because it also applies migrations.

Branch And Schema Compatibility

If you offer main and dev, users can move between them in both directions, so both branches must work with the same database:

  • Keep the same manifest id and config_path on every offered branch.
  • Make migrations additive and safe to run twice (CREATE TABLE IF NOT EXISTS, ADD COLUMN IF NOT EXISTS). Never drop or rename what another offered branch still reads.
  • Add a new migration file and its SCHEMA_MIGRATIONS entry to both branches before either offers it. A migration only on dev makes main refuse that database with schema_too_new.

Template updates and failed switches. Your copy does not follow template changes; check each change you port against both branches before offering it. If a launcher branch switch fails, the launcher may put the previous code back, but it does not undo migrations already applied to the database. Restore a backup if the old code then refuses it.

Full reference: SETUP.md, Back up and update and LAUNCHER_CUSTOM_MOD.md, Branches.

Next step: Publish your own mod.