Backup & Disaster Recovery¶
Vesana has a built-in backup system under Admin → System → Backup & DR.
It is the single standard — no separate sidecar, no manual pg_dump needed.
One system, four triggers
Every backup lands in ONE catalog, in ONE place, under ONE retention:
| Type | When |
|---|---|
| Scheduled | Daily full dump (on by default, 02:00 UTC) |
| Manual | "Back up now" button |
| Pre-restore | Automatically before every restore (reversibility) |
| Pre-update | Automatically before every online update by the updater |
A backup is a full pg_dump (compressed .sql.gz) including the TimescaleDB
hypertables (check history, logs). It lives in the Docker volume backup-data
(/var/lib/vesana/backups).
FIELD_ENCRYPTION_KEY is the only thing that truly cannot be replaced
Encrypted DB fields (SNMP communities, SNMPv3 and SSH passwords) can never be
decrypted again without this key. It must live outside the backup — in a
password manager + a printed copy in a safe. Keep the backup dump and the
FIELD_ENCRYPTION_KEY separate.
Schedule & retention¶
Under Admin → System → Backup & DR → Schedule & retention:
| Setting | Default | Meaning |
|---|---|---|
| Daily backup | on | Automatic full dump once per day |
| Hour (UTC) | 2 | Hour at/after which the daily backup is taken |
| Max count | 5 | older backups are pruned beyond this |
| Max age (days) | 90 | older backups are deleted |
| Max disk (GB) | 10 | total cap; oldest pruned until under it |
The strictest of the three retention limits wins. Pre-update and pre-restore backups count too. A watcher checks hourly whether the daily backup is due; a cleanup job runs every 6 h.
Off-site copy (important)¶
Backups live locally on this server. If the machine is lost entirely, so are they. For real disaster recovery you need an off-site copy:
- In the UI click "Download" on a backup → saves the
.sql.gzlocally. - Or copy it out of the volume with your own script:
docker run --rm -v vesana_backup-data:/src -v $(pwd):/dst alpine \
sh -c 'cp /src/*.sql.gz /dst/'
# then rotate to S3 / NAS / USB
Restore (on a running server)¶
In the UI: click "Restore" on a backup → type the hostname (type-to-confirm) → confirm 2FA (single-use token) → apply.
The restore is TimescaleDB-safe: it wraps the dump in timescaledb_pre_restore() /
timescaledb_post_restore() and terminates open DB sessions so hypertable chunks come
back correctly. A pre-restore backup of the current state is taken automatically first.
Restore = maintenance window
The restore overwrites the database and disconnects active sessions. All changes since the chosen backup are lost. Don't trigger it casually during operation.
Disaster recovery: fresh server¶
Goal: server crashes → fresh server → load yesterday's backup → back to yesterday's state, nothing lost except the current day.
- Provision a new server with
setup.sh(as for a first install). On first boot the migrations run → empty schema + TimescaleDB extension are in place. - Carry over
FIELD_ENCRYPTION_KEYfrom the old.env(otherwise encrypted fields are unreadable after the restore). LikewiseSECRET_KEYif existing logins should stay valid. - Log in as super-admin → Admin → System → Backup & DR.
- "Upload backup" → pick the
.sql.gzfrom your off-site copy. It shows up as a manual backup in the catalog. - Click "Restore" on the uploaded backup → type the hostname → 2FA → apply.
- Verify: log in with the original admin, open a host with an encrypted
snmp_community(value must be intact), check a service's check history (hypertable chunks must be present).
If an encrypted field looks empty/broken in step 6, the FIELD_ENCRYPTION_KEY was wrong.
Disaster drill¶
A restore you never test is hope, not a backup. Recommendation: run the fresh-server runbook on a separate VM once a quarter — including step 6.
See also¶
- Updates — the updater takes a pre-update backup into the same catalog before every update
- Security → Encryption — field encryption in detail