Backup and restore
Everything Tidings knows lives in one place: the data/ directory —
config.json plus the SQLite database, and the receipt files under
data/raw/attachments/ if you use receipts. A backup is a copy of that data;
a restore is putting it back. This guide covers both, plus leaving cleanly.
Under the default Docker stack, data/ is a named volume
(docker volume ls shows it as <project>_finance_data, e.g.
tidings_finance_data) — it is not the data/ folder in your checkout.
Copying the repo directory backs up nothing.
Back up
Section titled “Back up”The backup zip (recommended)
Section titled “The backup zip (recommended)”Settings → Backup → Download backup produces
finance-backup-<date>.zip: every transaction plus your configuration
(categories, overrides, budgets, settings). It is the portable format the
restore path understands, and it works the same on SQLite and DynamoDB.
The same thing over the API, for a cron job or an agent:
curl -sf -X POST http://localhost:8000/api/v1/data/export \ -H "Authorization: Bearer $FINANCE_API_TOKEN" \ -o "tidings-backup-$(date +%F).zip"Bearer tokens are covered in the agent access guide. Backup is disabled in demo mode — there is nothing of yours to back up yet.
What the zip does not carry: receipt and statement attachment files. The transactions and their links are in the zip; the images and PDFs themselves live only in the volume. If you use receipts, take a volume copy (below) as well.
The volume copy (full fidelity)
Section titled “The volume copy (full fidelity)”A byte-level copy of the whole volume — databases, config, attachments, saved briefings. Stop the stack first so SQLite isn’t mid-write:
docker compose stopdocker run --rm \ -v tidings_finance_data:/data \ -v "$(pwd)/backup":/out \ alpine cp -r /data /outdocker compose startSubstitute your volume name from docker volume ls. The result in
./backup/data/ is the entire state of your install.
Restore
Section titled “Restore”From the backup zip
Section titled “From the backup zip”Settings → Backup → Restore from backup. Upload the zip (or a plain transactions CSV from the Search tab’s export). Nothing is written until you confirm: a dry-run preview first shows how many rows are new versus duplicates, and you choose whether duplicates are skipped and whether the zip’s configuration is applied.
This is also the migration path — the zip restores into a fresh install on another machine, or across the SQLite/DynamoDB boundary.
From a volume copy
Section titled “From a volume copy”Onto a fresh install:
docker compose up -d # first boot creates the volumedocker compose stopdocker run --rm \ -v tidings_finance_data:/data \ -v "$(pwd)/backup/data":/from \ alpine sh -c "rm -rf /data/* && cp -r /from/. /data/"docker compose startOpen the dashboard and check the sidebar status dot; the troubleshooting guide covers anything that looks off.
A reasonable habit
Section titled “A reasonable habit”Download a backup zip before every upgrade (the upgrading guide starts there) and on some calendar rhythm that matches how much re-entry you can tolerate losing. One zip in your existing backup system — a synced folder, an external drive — is enough; this is a single household’s journal, not a database fleet.
Leaving Tidings
Section titled “Leaving Tidings”Everything is yours and everything is local, so leaving is short:
- Download a backup zip (transactions as portable JSON/CSV) if you want the history.
docker compose down -v— stops the stack and deletes the volume.- Delete the cloned repo directory.
- Revoke the Gmail App Password (myaccount.google.com/apppasswords) and turn off the bank-alert forwarding rules you created during email setup.
No accounts to close, nothing left behind.