Back up and restore the Hub¶
What you'll have at the end: encrypted database backups written on a schedule to durable storage, a manual backup you have watched succeed, and a restore procedure you have actually tested — before the day you need it.
Prerequisites: a running Hub you can log in to — see
Hub → Install procedure. On a bare (non-Docker) install,
the Hub host also needs the PostgreSQL client tools (pg_dump and psql);
the packaged Docker image already contains them.
Time: about 10 minutes for setup and the first backup; a restore test on a spare Hub adds perhaps 15 more.
All of this lives in one place: Settings → Data and recovery → Backups
(https://hub.example.internal/settings).
What a backup contains — and what it doesn't¶
A Cyphers backup is a complete dump of the Hub's PostgreSQL database
(pg_dump), compressed and encrypted, written as a single
<name>.sql.enc file. That covers everything the Hub stores in the
database: endpoints, certificates and observations, renewal history, scores,
audit log, settings — and the Hub's CA private keys, which are persisted
to the database (encrypted under the CA passphrase) as well as to disk, so a
restored database can re-provision the CA key files on its own.
It does not contain files that live outside PostgreSQL:
- The CA passphrase (
CYPHERS_CA_KEY_PASSPHRASE) and the rest of the Hub's environment file (.env:DATABASE_URL,CYPHERS_CA_URL, ports, NATS settings). Without the CA passphrase, the CA keys inside a restored database cannot be decrypted — keep a copy of the.envfile (or at minimum its secrets) in your secret store. - The backup passphrase itself. It is stored only encrypted; see the warning in Step 1.
- Scout-held material. Every Scout's private key, client certificate and delivered endpoint certificates live in the Scout's own data directory on each endpoint, never on the Hub — see Scout → Install procedure. A lost Scout is re-enrolled, not restored from a Hub backup.
Step 1 — Set the backup directory and passphrase¶
On Settings → Data and recovery → Backups, fill in the form and click Save backup settings:
- Backup directory — where backup files are written, as a path on the
Hub host. The default is
./backups, which sits next to the Hub itself — fine for a first test, but a backup on the same disk as the database dies with it. Point it at durable, off-box storage (a mounted network share, a separate volume that is itself backed up):
- Passphrase — the key every backup file is encrypted with. The field is write-only: it is never shown again, and leaving it blank on a later save keeps the current one.
You should now see the Schedule status line change from "No passphrase is set, so no backup can be written." to a schedule message, and the Back up now button become clickable.
The passphrase is required to restore — store it outside the Hub
A backup file is useless without the passphrase it was encrypted with, and the Hub stores that passphrase only in its own database — inside the very thing you are backing up. Put it in your password manager or secret store now, alongside the CA passphrase. Also: changing the passphrase later strands every backup encrypted under the old one, so if you ever rotate it, keep the old passphrase until its backups have aged out.
Step 2 — Enable the schedule¶
In the same form, tick Run backups on a schedule, set Every (hours) (default 24) and Backups retained (default 7 — how many completed backups the retention prune keeps, oldest deleted first after each successful backup), and click Save backup settings again.
You should now see the Schedule line read "Scheduled every 24 hours" (or your interval). The scheduler runs inside the Hub process and checks every minute whether the interval has elapsed since the last completed backup — there is no external cron to configure.
Retention only prunes what the schedule wrote
The keep-N rule applies to backups recorded in the Hub's history.
Other .sql.enc files in the directory (for example labelled
pre-upgrade dumps) are age-swept by the daily retention job instead, and
files in any other format are never deleted — only reported in the log.
Step 3 — Run a first backup and confirm it landed¶
Click Back up now.
You should now see, within a few moments, the Last backup status line show a fresh timestamp instead of "No backup has completed yet." — and the file on disk on the Hub host:
$ ls -l /mnt/backup/cyphers
-rw-r--r-- 1 cyphers cyphers 2469888 Aug 22 14:03 cyphers_backup_20260822_140301.sql.enc
If the attempt fails, the panel shows "Last attempt failed:" with the
reason, and the action reports "Backup failed; see backup history for the
reason". The most common first-run causes are a backup directory the Hub
process cannot write, or missing pg_dump on a bare install.
One-shot backups from the CLI
cyphers-hub backup --label pre-upgrade writes a
pre-upgrade-<timestamp>.sql.enc into the same directory, encrypted
with the same passphrase. It exists for pre-deploy rollback dumps — see
Fleet upgrades — and deliberately
does not reset the schedule clock.
Step 4 — Test a restore on a spare Hub (do this periodically)¶
An untested backup is a hope, not a backup. On a schedule of your choosing (quarterly is a reasonable floor), prove the whole chain — file, passphrase, restore — on a spare or staging Hub, never your production one:
- Stand up a scratch Hub against a scratch database (Hub → Install procedure).
- In its Settings → Data and recovery → Backups, save the same passphrase your production backups were encrypted with. The restore decrypts with the passphrase configured on the Hub doing the restoring — this step is also your proof that the passphrase in your secret store is the right one.
- Copy a recent
.sql.encfile onto the spare Hub's host. - Follow Step 5 below on the spare Hub.
You should now see, after the restore, your production data in the
spare Hub's dashboard — endpoints on Manage, history under
Activity — and http://hub.example.internal:8443/readyz answering with
all checks ok.
Restoring without any running Hub
For disaster recovery there is also a database-free path:
CYPHERS_BACKUP_PASS=<passphrase> cyphers-hub backup-decrypt-file
--input <file>.sql.enc --output dump.sql.gz decrypts and verifies the
envelope to a plain gzipped SQL dump you can feed to psql yourself.
It needs only the binary and the backup passphrase.
Step 5 — Restore¶
Restoring replaces the entire database
A restore replaces this Hub's entire database with the contents of the chosen file. Everything recorded after that backup was taken — observations, renewals, audit entries, settings changes — is gone. There is no partial or selective restore. On a production Hub, this is a last resort, not an undo button.
Open Restore from a backup at the bottom of the Backups panel, and:
- Enter the full path of the backup file (on the Hub host) in Backup file path.
- Type
RESTOREin the Type RESTORE field. - Click Restore.
The Hub decrypts and integrity-verifies the file before anything touches the database — a wrong passphrase, a truncated file, or a non-backup file is rejected with "Restore failed; the Hub is unchanged" and the database is left as it was. Only a file that decrypts and validates cleanly is then applied.
You should now see the dashboard reflecting the restored data, and
/readyz reporting every check ok.
Where to next¶
- Fleet upgrades — a pre-upgrade backup is half of the Hub rollback story.
- Hub → Install procedure — for standing up the spare Hub used in restore tests.