Backup and Restore
This guide covers backing up and restoring the Patrimoine SQLite database, including automated backups and offsite sync.
Internal Automatic Backup
Section titled “Internal Automatic Backup”The backend creates a SQLite backup on startup and every 24h using Python’s sqlite3.backup() (WAL-safe). Backups are saved to ./data/backups/patrimoine_<timestamp>.db (a backups/ directory next to the database file) with chmod 600 permissions.
This feature is controlled by the AUTO_BACKUP environment variable:
| Value | Behavior |
|---|---|
true |
Backup runs on startup + every 24h |
false (default) |
Backup task is not created — no CPU, no disk I/O, no lock contention |
AUTO_BACKUP=trueOpt-in: set AUTO_BACKUP=true to enable it. Set it back to false if the backup causes database lock contention (e.g., when multiple processes access the database simultaneously). The external backup-db.sh script (below) is not affected by this setting.
The internal backup is never pruned: delete old files in data/backups/ yourself, or rely on backup-db.sh, which keeps the last 30. With the arq worker the backup runs at 03:00 instead of at startup. It does nothing on PostgreSQL.
Online Backup (Recommended)
Section titled “Online Backup (Recommended)”The sqlite3 .backup command is WAL-safe — it does not interrupt the running service and produces a consistent snapshot.
# Create a backup inside the containerpodman compose exec backend sqlite3 /data/patrimoine.db ".backup '/data/patrimoine-backup-$(date +%Y%m%d).db'"
# Copy the backup to the hostcp ./data/patrimoine-backup-$(date +%Y%m%d).db ./backups/Stop-and-Copy
Section titled “Stop-and-Copy”Alternative method: stop the backend, copy the file, restart. This causes a brief service interruption but guarantees a quiescent database.
podman compose stop backendcp ./data/patrimoine.db ./backups/backup.dbpodman compose start backendRestore
Section titled “Restore”podman compose stop backendcp ./backups/backup.db ./data/patrimoine.dbpodman compose start backendAutomated Backup (backup-db.sh)
Section titled “Automated Backup (backup-db.sh)”The backup-db.sh script automates SQLite backups with rotation and optional offsite sync.
Manual Execution
Section titled “Manual Execution”./backup-db.shCrontab
Section titled “Crontab”# Daily backup at 03:000 3 * * * cd /path/to/patrimoine && ./backup-db.shWhat the Script Does
Section titled “What the Script Does”- Sets
umask 077— backup files are created with600permissions (owner read/write only). - Online backup — uses
sqlite3 .backupwith single-quote escaping in the path to prevent shell injection. chmod 600on the backup file.- Rotation — keeps the last 30 backups, deletes older ones.
- Offsite sync (optional) — uploads to WebDAV or S3 if configured.
Script Details
Section titled “Script Details”| Feature | Implementation |
|---|---|
| Shell | #!/bin/sh (POSIX) |
| Error handling | set -eu |
| File permissions | umask 077 + chmod 600 |
| Path escaping | Single quotes escaped via sed "s/'/''/g" |
| Rotation | ls -t ... | tail -n +31 | xargs rm -f (keeps last 30) |
| Backup dir | First argument (./backup-db.sh /path/to/dir); default ./data/backups |
| DB path | DATABASE_PATH environment variable; default ./data/patrimoine.db |
Offsite Sync (Optional)
Section titled “Offsite Sync (Optional)”The script supports two offsite sync methods, enabled via environment variables.
These variables are read only by backup-db.sh, from the environment of the host shell or cron job that runs it.
Export them in the shell, set them inline in the crontab line, or source a private file of your own from a small wrapper script:
# crontab: daily at 03:00 with WebDAV sync0 3 * * * cd /path/to/patrimoine && WEBDAV_URL=https://cloud.exemple.fr/remote.php/dav/files/user/backups/ WEBDAV_USER=user WEBDAV_PASSWORD=secret ./backup-db.shWebDAV
Section titled “WebDAV”# host environment of the backup-db.sh runWEBDAV_URL=https://cloud.exemple.fr/remote.php/dav/files/user/backups/WEBDAV_USER=userWEBDAV_PASSWORD=secretThe script uploads via curl using a temporary .netrc file:
- Creates a temp file with
mktemp. - Writes
machine,login, andpasswordentries. - Sets
chmod 600on the temp file. - Uploads with
curl --netrc-file. - Deletes the temp file after upload.
The password is never visible in ps output (it is read from the .netrc file, not passed as a command-line argument).
# host environment of the backup-db.sh runS3_BUCKET=mon-bucket-patrimoineRequires the aws CLI to be installed and configured on the host. The script uploads with aws s3 cp.
Both Methods
Section titled “Both Methods”WebDAV and S3 are independent — you can configure one, both, or neither. If no variables are set, the script performs only the local backup with rotation.
PostgreSQL
Section titled “PostgreSQL”If using PostgreSQL instead of SQLite (see PostgreSQL Deployment), use pg_dump instead of sqlite3 .backup:
docker compose -f docker-compose.yml -f docker-compose.postgres.yml exec postgres \ pg_dump -U patrimoine patrimoine > backup.sqlWith podman.sh: podman exec patrimoine-postgres pg_dump -U <user> <db> > backup.sql (user and database come from DATABASE_URL).
The backup-db.sh script is SQLite-only.
To move data from an existing SQLite deployment to a new PostgreSQL one, use the app’s own JSON backup export/import (the Export and Import pages) rather than pg_dump/.backup — see PostgreSQL Deployment: Migrating an Existing SQLite Deployment to PostgreSQL.
Application backup (JSON)
Section titled “Application backup (JSON)”Besides database-level backups, every user can download a per-account JSON backup from the Export page (GET /api/v1/export/backup) and restore it from the Import page (POST /api/v1/import/backup). The file carries a version (currently 15), and a backup written by an older version restores into a newer one. This backup covers one user’s data only and is not a substitute for a database backup. See the User Manual’s Import & Export page.
See Also
Section titled “See Also”- Podman Compose Deployment
- PostgreSQL Deployment
- Environment Variables
- Debugging — Database lock troubleshooting
- Database Architecture — SQLite WAL mode, multi-process access warning