Backups and restore
The database is encrypted at rest and the key is not recoverable. A backup without its key is noise. Every archive the server makes includes the key for that reason, which makes the archive itself a secret: keep it somewhere private.
From the admin panel
Admin, Server settings, Backup: create an archive, verify one, download it, delete it, or restore from it. Archives land in ODM_BACKUP_DIR (default ~/odm-backups). A world hosted from the desktop or Android app has no Backup section; see Hosting from your computer for keeping a copy of a desktop world.
From the command line
node scripts/odm-backup.mjs # to ~/odm-backups
node scripts/odm-backup.mjs /path/to/backups
node scripts/odm-restore.mjs odm-backup-<stamp>.tar.gz /tmp/odm-restore-check # dry run
node scripts/odm-restore.mjs odm-backup-<stamp>.tar.gz --liveUnder Docker, run each through ./scripts/docker-entrypoint.sh inside the container so the key is loaded. Stop the app before a command-line backup; the live restore refuses to run while the server is listening.
What a backup does
- Opens the encrypted database with the key to prove it is readable, and checkpoints the write-ahead log.
- Stages the persistent state:
data/(the database, world packs, the Docker key file), uploaded and generated media, local model files, and.env.serverwhen present. - Writes a manifest and a timestamped
odm-backup-<stamp>.tar.gz, and prints its SHA-256 (archives over 2 GiB are hashed as a stream).
What a restore does
A restore verifies before it touches anything: it rejects absolute and parent-traversal paths in the archive, extracts to a temporary folder, reads the archived key, opens the database, runs an integrity check, and reports the campaign, user and message counts it found. The dry run stops there. --live replaces the running install's state with the archive's, and refuses while the app is listening on its port.
Manual copies
If you would rather copy files: both data/local-roleplay.sqlite and its key are required, plus public/uploads/; add data/worlds/ if packs were installed. public/generated* is optional (pictures and audio can be regenerated). Under Docker the odm-data, odm-uploads, odm-generated and odm-audio volumes are the same set.
Per-item exports
- A character as
.odm-character.json, portrait inside. - A workshop as
.odm-workshop.json, pictures inside. - A campaign's story as HTML, ODT or DOCX: a reading copy, not a save.
These import on any server. Play state (the chronicle, the seats, the live sheets) moves only with the database.
When to back up
Before every update, and after a session you would mind losing. A world hosted on a phone cannot be archived: export what you want to keep, and remember that uninstalling the app deletes the world.