Type: how-to · Last reviewed: 2026-07-18
Applies to: Pulse Historian · edge/on-prem · managed cloud
Goal: confirm automated backups are running, take/inspect a backup, and restore the SQLite database or the time-series data from an archive.
Prerequisites:
/api/admin/backup/* routes require the admin role. See Query from the terminal § Get a token.https://<plant-server>:3030).Backups and HA solve different problems: HA keeps you running when a node dies; backups let you recover from corruption, a bad delete, or a full-site loss. Run both.
Two independent stores, each on its own schedule (defaults):
| Store | Archive | Default cadence | Retention |
|---|---|---|---|
SQLite app DB (pulse-db.sqlite) |
sqlite/sqlite-<ts>.tar.gz |
every 180 min | rolling, keep 6 |
Time-series data (data/) |
timeseries/timeseries-incremental-<ts>.tar.gz |
every 24 h, incremental | no auto-cleanup |
Each archive gets a .tar.sha256 sidecar checksum. The scheduler runs on its own thread from startup. Full internals → Backup System.
curl -k https://plant-server:3030/api/admin/backup/status -H "Authorization: Bearer $ADMIN_TOKEN"
{ "last_sqlite_backup": { "success": true, "file_name": "sqlite-2026-07-18_030000.tar.gz", "file_size": 184320 },
"last_timeseries_backup": { "success": true, "file_name": "timeseries-incremental-2026-07-18_030000.tar.gz" } }
curl -k https://plant-server:3030/api/admin/backup/list -H "Authorization: Bearer $ADMIN_TOKEN"
{ "sqlite_backups": [ { "name": "sqlite-2026-07-18_030000.tar.gz", "size": 184320, "created": "…", "checksum": "…", "is_incremental": false } ],
"timeseries_backups": [ … ] }
curl -k -X PUT https://plant-server:3030/api/admin/backup/config \
-H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \
-d '{"sqlite_backup":{"enabled":true,"interval_minutes":180,"num_backups_to_keep":6},
"timeseries_backup":{"enabled":true,"incremental_only":true}}'
{ "success": true }
Backups are triggered by the scheduler when due — there is no "back up now" endpoint. To force one, shorten interval_minutes and wait for the next scheduler tick. Full schema → Backup System § BackupConfig.
Restoring overwrites the live store. Do it during a maintenance window. The old DB is renamed to
pulse-db.sqlite.bakfirst (not auto-cleaned).
curl -k -X POST https://plant-server:3030/api/admin/backup/restore/sqlite \
-H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \
-d '{"file_name":"sqlite-2026-07-18_030000.tar.gz"}'
{ "success": true, "message": "SQLite restored successfully" }
The existing data/ directory is renamed to data_backup_temp/ before extraction (not auto-cleaned).
curl -k -X POST https://plant-server:3030/api/admin/backup/restore/timeseries \
-H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \
-d '{"file_name":"timeseries-incremental-2026-07-18_030000.tar.gz"}'
{ "success": true, "message": "Timeseries restored successfully" }
Restart the server after a restore so it re-opens the restored files cleanly.
GET /api/admin/backup/status shows a recent success: true for the store you care about..tar.sha256 sidecar matches the archive (sha256sum -c) — the SQLite checksum is over the raw DB bytes; the time-series checksum is over the compressed archive.steam_pressure) and confirm the expected history is present — see Query from the terminal.| Symptom | Cause | Fix |
|---|---|---|
401 / 403 |
Non-admin token | Use an admin account |
Restore success: false |
Archive name wrong, or file missing at the backup path | Copy the exact name from /backup/list; check CLARITY_BACKUP_PATH |
| Timeseries backup keeps skipping | Incremental found no new .bin files |
Expected when nothing changed since the last backup |
| Disk filling with timeseries archives | No auto-cleanup for timeseries | Prune old timeseries-*.tar.gz manually |
| Backups stopped | enabled:false, or backup path unwritable |
Check /backup/config; verify disk/permissions at the path |