Backups and restore

Verified backups from the admin panel or the command line, what an archive contains and why it is a secret, dry-run and live restores, manual copies, per-item exports, and the one thing you cannot recover without: the encryption key.

ServerDesktop appAndroid app

The database is encrypted at rest and the key is not recoverable. A without its key is noise. Backups copy .env.server and data/.db-key when those files exist. A key supplied only through the process environment is not automatically written into the archive: keep that matching key separately. Treat archives as private because their database and configuration can contain secrets.

From the admin panel

Admin, Server settings, : 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 apps uses in App settings instead of this Backup section.

Move your world

In the desktop or Android app, open Settings, Your world on this computer/device, . Export world saves the whole as an odm-world-*.tar.gz file: accounts, campaigns, characters, prep, installed , pictures and cached . Keep the app open until it finishes.

On the destination device, leave any table first and choose Import world. The app checks the file, previews its contents and asks before Replace my world. Import replaces the destination world and restarts its server; the previous world stays set aside until the next import. The owner is signed in after the swap. Provider keys and sign-ins are excluded: enter your AI key again in Story AI and let friends sign in again. The exported campaigns are readable by anyone holding the file, so keep it private.

This is the portable device-world format, separate from a self-hosted server's . records travel, but addon packages need reinstalling on a supported destination.

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 --live

Under , run each through ./scripts/docker-entrypoint.sh inside the container so the key is loaded. Stop the app before a command-line ; the live restore refuses to run while the server is listening.

What a backup does

  1. Opens the encrypted database with the key to prove it is readable, and checkpoints the write-ahead log.
  2. Stages the persistent state: data/ (the database, , the key file), uploaded and generated media, files under models/, and .env.server when present. The default data/addons/ folder is included; files under a custom directory outside the managed paths need a separate copy. The admin can omit downloadable files under models/ unless you choose to include them.
  3. 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 checks archive paths, extracts to a temporary folder, opens the database, runs an integrity check and reports its campaign, user and message counts. The command-line restore requires the matching key inside the archive's .env.server or data/.db-key. The admin restore can fall back to the current server's key when the archive has none; verification on that server therefore does not prove the archive contains its own key.

For a portable command-line backup, make sure one of those files contains the actual key before taking the backup. Keep the original value separately as well when your deployment supplies it only through the process environment.

The command-line dry run stops after verification. --live stages the replacement, swaps managed paths by rename and retains the previous state; a failed swap rolls back. It refuses while the app is listening. Check the output for the retained paths before removing any previous copy.

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 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 as .odm-workshop.json, pictures inside.
  • A campaign's story as HTML, ODT or DOCX: a reading copy, not a save.

Character and workshop files import on other servers; story documents are reading copies. Play state (the , 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. The apps' export works on phones too. Export before switching between and Play installs, since uninstalling deletes the private world.

Checked against desktop and Android app 0.19.4 and server 0.28.4 on 2026-10-21. Something wrong or missing? Open an issue.

Download · v0.21.2
Server app · v0.30.2

Optional. Runs the table for a whole group on one machine that stays on. Everyone else just installs the app.

Free & open source · MIT licensed · served from GitHub releases