A backup you have never restored is a hope. Here a backup is tested by loading it into a scratch database and re-checking that the ledger still balances.
What to keep
A complete copy is three things, kept together but stored separately where it matters.
- The database. Every entity's books.
- The documents. Uploaded files live outside the database, under
DOCUMENT_STORAGE_ROOT. A database backup does not contain them. - The credentials key.
LEDGER_CREDENTIALS_KEYdecrypts the integration keys you saved in Settings. A database backup is useless for those without it, and the key must not live only next to the backup.
Encrypt backup storage and any offsite copy, set a retention period, and test a restore in a scratch environment. The application does not encrypt PostgreSQL records or documents for you.
From source
npm run db:backup writes a consistent logical snapshot of every table to the backups/ folder. It reads inside one serializable, read-only transaction, so the copy is a single point in time and takes no write locks. The folder is gitignored, so you can point a sync tool such as OneDrive at it.
npm run db:restore proves the backup. It rebuilds the schema in a scratch database, loads the snapshot, re-applies the triggers and re-checks that posted debits equal credits and that entity and account counts match. It prints PASS or FAIL and exits non-zero on failure. It never touches your live database.
npm run db:backupnpm run db:restoreWith no argument, restore uses the latest backup. To restore a particular one, pass its folder name after --.
Take a backup before a migration or any bulk change, and prove it restores. Never run the reset or recreate commands on books you care about: they drop the database.
The snapshot covers the database only. Copy your document folder and store the credentials key separately.
On Windows
Ledger protects every upgrade for you. When the app version changes, it takes a pg_dump backup, restores it into a scratch database, checks that posted entries balance, migrates only the copy and switches over after a second check. The backup carries a .dump.verified.json receipt. The old database stays in the cluster as ledger_previous_*.
That covers the database during an upgrade. It does not cover documents or the credentials key, and nothing is copied off your machine.
For a complete manual backup:
- Close Ledger and confirm that it has stopped.
- Copy the entire state folder,
%LOCALAPPDATA%\Analytical Ledger, to encrypted offline storage. - To restore, close Ledger and put that complete copy back. Keep the failed state separately for diagnosis.
Keep the state folder whole, including secrets.json. There is no scheduled or off-machine backup service in the Windows package, so decide how often you copy it. Install on Windows has the layout.
On a hosted instance
The operator runs backups for you. In the hosting kit's defaults, a backup service creates a verified recovery set at 02:30 UTC every night and keeps 30 days locally. Verification includes an actual restore into a scratch database that checks parity, constraints and triggers, ledger balances and document hashes.
Local backups alone do not survive the loss of the machine. An offsite copy is a separate decision that you and the operator make before activation, and no offsite provider is switched on silently.
Ask your operator for:
- the retention period and the offsite destination
- a restore drill at least monthly, with the result recorded
- the age of the last local and offsite backup
An operator runs a drill like this on the server:
bash deploy/hosted/backup-verify.sh <slug>Run a restore drill
Do this on a schedule, not for the first time in an emergency.
- Take a fresh backup.
- Restore it somewhere that is not your live system: the scratch restore that
npm run db:restoreperforms, or an isolated stack. - Read the verdict. A pass means posted debits equal credits and the counts match.
- Open the restored books and spot-check a report you know.
- Confirm you can also restore the documents and the credentials key that go with it.
- Record the date and the result.
Never run a live restore over the production database without explicit approval.