Backup & Restore
This page covers routine backups and restoring from them.
What to back up
| Path/volume | What's in it | Backup? |
|---|---|---|
Database (mysql_data, pg_data, etc.) |
User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | Critical: back this up nightly. |
/romm/assets |
User uploads: save files, save states, user-uploaded screenshots, manuals, covers | Critical: back this up nightly. |
/romm/config |
config.yml and any custom overrides |
Critical: rarely changes but small and painful to recreate. |
/romm/resources |
Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Medium priority, and can be re-downloaded on a rescan (including it speeds up recovery). |
/redis-data |
Task queue state | Low priority, in-flight tasks only, and lost tasks can be re-run. |
/romm/library |
Your ROM files | Back this up separately. It's your source data and you should already have a backup strategy for it. |
Routine backup
Always stop the stack or use consistent snapshots
A live cp -r of the DB volume can copy an inconsistent state. Either bring the stack down briefly, or use mysqldump/pg_dump for a consistent logical dump.
DB dump (MariaDB/MySQL)
docker exec romm-db mariadb-dump \
--user=romm-user --password=<DB_PASSWD> \
--single-transaction --databases romm \
> romm-db-$(date +%F).sql
DB dump (PostgreSQL)
Assets + config
Copy the host paths you mounted as /romm/assets and /romm/config. Incremental rsync is fine:
rsync -a --delete /srv/romm/assets/ /backup/romm/assets/
rsync -a --delete /srv/romm/config/ /backup/romm/config/
Putting it together
A minimal nightly cron:
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F)
DEST=/backup/romm
docker exec romm-db mariadb-dump --user=romm-user --password=$DB_PASSWD \
--single-transaction --databases romm | gzip > "$DEST/db-$STAMP.sql.gz"
rsync -a --delete /srv/romm/assets/ "$DEST/assets/"
rsync -a --delete /srv/romm/config/ "$DEST/config/"
# keep 14 days of DB dumps
find "$DEST" -maxdepth 1 -name 'db-*.sql.gz' -mtime +14 -delete
Send it offsite however you already do (rclone to B2/S3, restic, borg). Remember the 3-2-1 rule: 3 copies, on 2 different media, with 1 offsite!
Restore
Into an empty install
- Start a fresh stack with the same
ROMM_AUTH_SECRET_KEY(if the secret changes, all sessions and invite links are invalidated). - Wait for the first-run setup to finish, then stop the stack:
docker compose stop. - Restore the DB:
docker exec -i romm-db mariadb --user=root --password=<root-pw> romm < romm-db-2026-04-15.sql
# or for Postgres:
docker exec -i romm-db psql --username=romm-user --dbname=romm < romm-db-2026-04-15.sql
- Restore
assets/,config/andresources/to the mounted host paths. - Restart with
docker compose start.
Verifying a backup is actually restorable
A backup you haven't restored is a hope, not a backup. Spin up a throwaway stack from a recent backup twice a year: