Skip to main content

Backup & Restore

O.D.I.N. stores all configuration, job history, spool inventory, and user data in a single SQLite database. This page explains how to create backups and restore from them.


Database Location

The database lives inside the Docker volume:

FilePurpose
/data/odin.dbMain database (SQLite WAL mode)
/data/odin.db-walWrite-Ahead Log — uncommitted transactions
/data/odin.db-shmShared memory file for WAL coordination

All three files are part of the live database. You must handle them together.

Do Not Copy .db Directly While the Container is Running

Copying the raw .db file while the container is running will produce an inconsistent snapshot — the WAL and SHM files contain in-flight transactions that will not be in the copy. This can cause data corruption on restore.

Always use the backup methods below, which use SQLite's .backup() API to produce a consistent point-in-time snapshot.


Backup via UI

  1. Log in as admin
  2. Go to Settings → System
  3. Under the Backup section, click Create Backup
  4. A .db snapshot is created in /data/backups/
  5. Click Download next to the backup to save it to your local machine

Backups are named with a timestamp (e.g., odin_backup_20260225_143000.db).


Backup via Command Line

Use docker exec to trigger a backup, then docker cp to retrieve it:

# Trigger a backup (creates a timestamped file in /data/backups/)
docker exec odin python3 -c "
import sqlite3, datetime, os
ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
src = sqlite3.connect('/data/odin.db')
dst = sqlite3.connect(f'/data/backups/odin_backup_{ts}.db')
src.backup(dst)
dst.close()
src.close()
print(f'Backup created: odin_backup_{ts}.db')
"

# Copy the latest backup to your local machine
docker cp odin:/data/backups/odin_backup_20260225_143000.db ./odin_backup.db

To list all existing backups:

docker exec odin ls -lh /data/backups/

Scheduled Backups (Cron)

Add a cron job on your host to trigger a backup nightly:

# Run at 2:00 AM daily
0 2 * * * docker exec odin python3 -c "
import sqlite3, datetime
ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
src = sqlite3.connect('/data/odin.db')
dst = sqlite3.connect(f'/data/backups/odin_backup_{ts}.db')
src.backup(dst)
dst.close()
src.close()
" && docker cp odin:/data/backups/odin_backup_$(date +\%Y\%m\%d)_020000.db /opt/odin-backups/

Alternatively, schedule a host-level volume backup by snapshotting the Docker volume directory directly — this is safe when the container is not running.


Data Retention

Backups are not automatically pruned. You are responsible for managing the /data/backups/ directory size. To delete old backups from the CLI:

# Delete backups older than 30 days
docker exec odin find /data/backups/ -name "*.db" -mtime +30 -delete

Restore via UI

  1. Log in as admin
  2. Go to Settings → System → Restore
  3. Click Upload Backup File and select a .db file
  4. O.D.I.N. will create a safety backup of the current database before overwriting
  5. After restore, the container restarts automatically
Test Restores on a Separate Instance

Before relying on a backup for disaster recovery, test it by restoring to a second O.D.I.N. instance (a test Docker container on the same host with a different port mapping). This confirms the backup is valid and not corrupted.


Restore via CLI

If the UI is unavailable (e.g., database is corrupted), restore manually:

# 1. Stop the container
docker compose down

# 2. Copy your backup into the volume
docker cp ./odin_backup.db odin-data:/odin.db
# If using a bind mount:
cp ./odin_backup.db /opt/odin/data/odin.db

# 3. Remove the stale WAL and SHM files
rm -f /opt/odin/data/odin.db-wal /opt/odin/data/odin.db-shm

# 4. Start the container
docker compose up -d

See Also