Skip to content

Data and backup

mcinquin edited this page Oct 12, 2026 · 1 revision

Data and backup

Everything is stored in data/ (gitignored), mounted as /app/data in Docker:

  • data/hobbyhoops.db — SQLite (collection, references, accounts, sessions, rate limiting) via better-sqlite3
  • data/card-photos/ — card front/back images (JPEG / PNG / GIF / WebP as uploaded)
  • data/backups/ — local database backups (gitignored)

This file and its WAL journals are local and must never be committed. Card photos live next to the database on the same volume — back up data/ as a whole if you want images included.

On first launch, an empty database is created; create the administrator account from the login screen.

Backup and restore

The SQLite file is the single source of truth. The database runs in WAL mode (hobbyhoops.db plus optional -wal / -shm sidecar files). Back it up on the host (the production container has no sqlite3 CLI). Keep a local script or cron on the server — it is not versioned in this repository.

Backup

Use SQLite’s online backup API (consistent snapshot, safe while the app is running). Do not copy hobbyhoops.db with cp while the application is open; that can produce an incomplete file.

# Prerequisite: sqlite3 (apt install sqlite3)
mkdir -p data/backups
sqlite3 data/hobbyhoops.db ".backup 'data/backups/hobbyhoops-$(date +%Y-%m-%dT%H-%M-%S).db'"
# Optional: delete backups older than 14 days
find data/backups -maxdepth 1 -name 'hobbyhoops-*.db' -mtime +14 -delete

Daily cron example:

0 3 * * * cd /path/to/hobbyhoops && mkdir -p data/backups && sqlite3 data/hobbyhoops.db ".backup 'data/backups/hobbyhoops-$(date +\%Y-\%m-\%dT\%H-\%M-\%S).db'" && find data/backups -maxdepth 1 -name 'hobbyhoops-*.db' -mtime +14 -delete >> /var/log/hobbyhoops-backup.log 2>&1

Restore

Stop the application first, replace the database file, then delete stale WAL journals. Skipping the last step (or restoring while the app is still running) can corrupt the database (SQLITE_CORRUPT / database disk image is malformed).

Production:

docker compose stop app
cp data/backups/hobbyhoops-YYYY-MM-DDTHH-MM-SS.db data/hobbyhoops.db
rm -f data/hobbyhoops.db-wal data/hobbyhoops.db-shm
docker compose start app

Local development (npm run dev):

# Stop the dev server (Ctrl+C), then:
cp data/backups/hobbyhoops-YYYY-MM-DDTHH-MM-SS.db data/hobbyhoops.db
rm -f data/hobbyhoops.db-wal data/hobbyhoops.db-shm
npm run dev

Corruption recovery

If the database is already corrupted after a bad restore, stop the app and run:

node scripts/recover-sqlite.mjs

This copies readable tables into a fresh file, rebuilds references_state from cards, and recreates the FTS index. The broken file is kept as data/hobbyhoops.db.corrupt-<timestamp>.

CSV vs SQLite backup

CSV export (Admin → CSV import / export) complements the SQLite backup:

  • SQLite — full instance (sessions, accounts, wanted, etc.)
  • CSV — portability and spreadsheet editing of the collection only

See CSV-import-export. Also SECURITY.md.

Clone this wiki locally