Backups
moth keeps its entire state in one place, and ships first-class tooling to
snapshot and restore it: moth backup and moth restore. The only
subtlety is where the master key lives.
What to back up
Section titled “What to back up”Two things, and they are backed up differently:
-
The data directory —
data/by default (--data-dir):data/moth.db SQLite database (users, projects, tokens, events)keys/ master.keyuploads/ project logo assets -
The master key —
data/keys/master.key, or whatever you inject viaMOTH_MASTER_KEY.
The master key encrypts every project’s signing private key and all provider secrets at rest. A database backup without the master key is unrecoverable — you’d restore encrypted blobs you can’t decrypt.
Taking a backup
Section titled “Taking a backup”moth backup writes a single gzip-compressed tar archive with an
online-consistent snapshot of the database (taken with VACUUM INTO, so
it is safe against a live server — no need to stop it), plus uploads/
and the key material:
moth backup --data-dir /var/lib/moth --to /backups/moth-$(date +%F).tar.gzRestore it with moth restore (below). The archive is self-contained, so
by default it includes master.key — which makes it as sensitive as
the master key itself. Store it encrypted and access-controlled, not in a
shared bucket. (If you inject the key via MOTH_MASTER_KEY instead of a
key file, there is no master.key on disk to bundle, and the archive
carries only the database and uploads — back the KMS entry up separately.)
Automate it from cron or a systemd timer:
# daily at 03:30, keeping the last 14 days30 3 * * * moth backup --data-dir /var/lib/moth --to /backups/moth-$(date +\%F).tar.gz \ && find /backups -name 'moth-*.tar.gz' -mtime +14 -deleteOr let the server take them itself — set backup_dir (and optionally
backup_interval, default 24h) in the config and moth serve writes
scheduled snapshots without any external scheduler.
Manual alternative
Section titled “Manual alternative”If you’d rather not use moth backup, SQLite’s online backup is also safe
against a running instance — don’t copy moth.db byte-for-byte while the
server runs (WAL mode can leave a plain copy inconsistent):
sqlite3 /var/lib/moth/data/moth.db ".backup '/backups/moth-$(date +%F).db'"Then archive uploads/ and confirm your master.key is in your secrets
store.
Restoring
Section titled “Restoring”From a moth backup archive:
moth restore /backups/moth-2026-07-18.tar.gz --data-dir /var/lib/mothmoth restore recreates the database, uploads, and keys in the data
directory. For safety it refuses to write into a non-empty data
directory unless you pass --force, so an accidental restore can’t
clobber a running instance — stop moth serve before restoring over
existing data.
A few things to keep in mind:
- Install the same (or newer) moth version — migrations only ever move the schema forward, so a newer binary opens an older database; the reverse is not supported.
- If your backup did not include the master key (you inject
MOTH_MASTER_KEY), make the same key available — it’s the only thing that can decrypt the restored signing keys and provider secrets. - Start moth and run
moth doctor— it confirms the JWKS is served and provider configs still verify.
If you’ve restored to a new host with a different public URL, update
base_url before creating or issuing anything: it’s baked into token
iss, JWKS URLs, email links, and OAuth redirects
(configuration).
Moving users, not the instance
Section titled “Moving users, not the instance”To copy users between projects or instances — rather than restore a whole instance — use the migration import/export flow, which is a portable JSON document rather than a database snapshot.