Backups and restore
This page covers the nightly backup and how to rehearse a restore before you need one.
Before you begin
- An absolute offsite folder that a sync client (Google Drive, Dropbox, iCloud Drive, a mounted share) copies off the machine, with its parent already existing.
- The task database service running; task databases are backed up online.
- The stack's backup worker (
deploy/compose/backup.compose.yaml), whichpnpm os:setupincludes unless given--without-backup. Its contract is on GitHub: https://github.com/notice-cx/NoticeOS/blob/main/deploy/compose/backup.README.md.
What is backed up
One backup run captures every store of the installation into a dated folder, .local/backups/<YYYY-MM-DD>/:
| Part | What it holds |
|---|---|
postgres/noticeos.dump | A pg_dump of the operational database, in custom format, mode 0600 |
r2/ | The raw provider archive: report files plus their index |
beads/ | Every task database, taken online from the running task service, with its permissions and recovery credentials under beads/metadata/ |
assets/ and cloudflare-d1/ | Production exports for sites you have explicitly approved |
recovery/ | The key that decrypts stored provider credentials, the database passwords and the task service settings. Only the local set has it: the offsite copy leaves it out, so keep a copy of this folder in a password manager |
RESTORE.md | Generated instructions: what the set is, how each part went, how to restore it |
backup.json | The completeness record, written last |
Each part is consistent on its own, not as one transaction across all of them. One part's failure never stops the others from being attempted, but only a complete set counts as a successful backup.
When it runs
The stack takes a backup nightly at 04:00 UTC; pnpm os:backup takes one now.
Set where it goes and how long it is kept
The backup settings are host-backup.json in the installation folder's backup/profile/, beside the worker's profile. pnpm os:setup -- --offsite <folder> writes one; inside the worker, that folder is /backup-offsite. Without it, the product default has no offsite folder. To copy the backups to another folder on this machine, change NOTICEOS_BACKUP_OFFSITE_PARENT in installation.env and run pnpm os:apply.
In
host-backup.json, set the offsite folder and the retention:json{ "offsiteBackupDir": "/backup-offsite/backups", "retention": { "daily": 2, "weekly": 2 }, "analyticalHistoryDirectory": null, "assetBackups": [] }Leave the service running. The file is read at every run; an edit applies at the next backup without a restart.
offsiteBackupDiris the folder, by its path inside the worker, that the sync client copies off the machine. Each finished set is copied under<offsiteBackupDir>/<date>/. Its parent must already exist; a missing sync mount fails the run instead of creating a local folder that is never synced.nullmeans no offsite copy, and the record shows that.retentionkeeps the latestdailycomplete sets and the first complete set of each of the latestweeklyweeks, in both the local and offsite trees. Without it, sets older than 30 days are removed.
Retention runs only after success
Old sets are removed only after every stage, including the offsite copy, succeeds. A failed run removes nothing.
Change the time or run one now
Open System health > Background operations > Backups and select Edit schedule to change the time.
To run one now, in the checkout, run:
shpnpm os:backup
Add site production exports
- In
host-backup.json, list underassetBackupseach repository whose ownbackup:prodscript exports a production database into the set. Each entry pins the script's hash, so a changed script fails before it runs. - For Cloudflare D1 databases, open Integrations > Cloudflare, connect the account and select the databases there instead.
Adding a repository to Tasks never authorizes a production export; only this file or that page does. Review the impact on the live database before enabling either.
Rehearse a restore
Restore into isolated stores first, never over the live ones.
- In the dated folder, open
RESTORE.md. It lists each part and its result. - Postgres: prepare the roles and an empty owner-owned database, then restore the dump into it without applying migrations first. The exact steps are on GitHub: https://github.com/notice-cx/NoticeOS/blob/main/db/postgres/host/README.md#backup-and-restore.
- Raw archive: restore
r2/as one complete tree. - Task databases: restore into a new, empty task service. The helper stops instead of overwriting an existing service or volume. Steps: https://github.com/notice-cx/NoticeOS/blob/main/db/dolt/host/README.md#backup-and-recovery.
- Check row counts, ledger totals and archive references before you point an app at the restored stores.
Replacing the live stores is a separate operator decision: stop the stack first (pnpm os:stop), restore, then start it again (pnpm os:start). A restore does not run migrations and a migration does not restore; keep the two steps apart.
For a demo installation, the procedure is a stopped backup of its four volumes; see Try the demo.
Verify
- The dated folder holds
backup.json, written last, andRESTORE.mdlists the result of each part. - The finished set is copied under
<offsiteBackupDir>/<date>/. - System health > Background operations shows the backup job's latest result.
A complete set does not prove the offsite copy is synchronized or restorable; a completed offsite handoff proves the local copy was made, not that the sync client finished uploading. Only your rehearsal proves a restore. A backup never deletes any source rows; a complete backup is never a reason to delete rows from the store.
If it didn't work
- Start with the backup report,
state/.local/backup-report.jsonin the installation folder. It lists each part's result and the reason a part failed. Every run replaces it; it is readable only by the installation's owner and is never copied into a backup set. - The backup failed on Offsite handoff: the folder in
host-backup.jsonhas no existing parent; usually the sync mount is missing. Fix the mount. The local set is kept. - The backup failed on Task hub: the task database service was not running. Start it and run
pnpm os:backup. - A concurrent attempt fails before copying: a backup does not run while another is in progress. Wait for it to finish.
More symptoms are in Troubleshooting. The operation's full contract is on GitHub: https://github.com/notice-cx/NoticeOS/blob/main/scripts/README.md#backups--restore and https://github.com/notice-cx/NoticeOS/blob/main/config/host-backup.README.md.