Skip to content

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), which pnpm os:setup includes 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>/:

PartWhat it holds
postgres/noticeos.dumpA 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.mdGenerated instructions: what the set is, how each part went, how to restore it
backup.jsonThe 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.

  1. In host-backup.json, set the offsite folder and the retention:

    json
    {
      "offsiteBackupDir": "/backup-offsite/backups",
      "retention": { "daily": 2, "weekly": 2 },
      "analyticalHistoryDirectory": null,
      "assetBackups": []
    }
  2. Leave the service running. The file is read at every run; an edit applies at the next backup without a restart.

  • offsiteBackupDir is 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. null means no offsite copy, and the record shows that.
  • retention keeps the latest daily complete sets and the first complete set of each of the latest weekly weeks, 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 ​

  1. Open System health > Background operations > Backups and select Edit schedule to change the time.

  2. To run one now, in the checkout, run:

    sh
    pnpm os:backup

Add site production exports ​

  1. In host-backup.json, list under assetBackups each repository whose own backup:prod script exports a production database into the set. Each entry pins the script's hash, so a changed script fails before it runs.
  2. 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.

  1. In the dated folder, open RESTORE.md. It lists each part and its result.
  2. 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.
  3. Raw archive: restore r2/ as one complete tree.
  4. 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.
  5. 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, and RESTORE.md lists 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.json in 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.json has 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.

Next steps ​

Released under the AGPL-3.0 license.