Backup & upgrades
Taranac backs up its configuration, not its history. A backup captures everything that defines how the system authenticates and authorises — devices, policies, profiles, users, groups, RBAC, the internal CA, daemon configs and the host secrets needed to bring the stack back up — and packages it into a single encrypted archive. Logs, live sessions, alerts and other dynamic data are deliberately excluded.
Backups are created and managed from the admin UI under Settings → Backup & Recovery. Restore is not in the UI — it is destructive (it erases the current configuration) so it runs from a CLI, off the API, and verifies your master key before it touches anything. This page covers what is in an archive, scheduling and retention, the two-phase recovery flow, how the upgrade/version gate works, and how backups relate to high availability.
- dbalways
- certs
- host_files
- daemon_configs
What is in a backup
Section titled “What is in a backup”An archive is named taranac-backup-<UTC-timestamp>.tar. It is an outer tar with two members:
manifest.json— plaintext, non-secret metadata. It records the format version, creation time, trigger (manual/scheduled), the Taranac version (app_version), the database schema revision (alembic_head), a short master-key fingerprint, and an encrypted canary token. Because it is readable without the key, recovery can verify the right master key before decrypting anything.payload.enc— the actual contents, an inner tar encrypted with the master key (Fernet).
The encrypted payload contains:
| Component | Badge | What it is | Always included? |
|---|---|---|---|
| Database dump | db | A data-only dump of the configuration tables (dynamic tables excluded) | Yes — not a toggle |
| Certificates | certs | The internal CA plus all server key/certificate pairs | Optional (include_certs) |
| Host files | host_files | The active .env file and Docker Compose file — i.e. the non-database secrets: master key, API keys, DB password | Optional (include_host_files) |
| Daemon configs | daemon_configs | Generated TACACS+ / RADIUS / NAC config snapshots (informational — regenerated from the DB on restore) | Optional (include_daemon_configs) |
A leaked archive is useless without the master key.
Configuration vs dynamic data
Section titled “Configuration vs dynamic data”The boundary between “config” (backed up) and “dynamic” (excluded) is defined in one place in the product, so the backup side and the restore side never disagree. Excluded from the database dump:
- Log tables (authentication, sessions, accounting, audit logs, LDAP sync logs)
- Live NAC sessions, the alert queue and firing alerts, MFA challenges
- Ephemeral tokens (refresh tokens, password-reset tokens, certificate-download tokens) and password-reuse history
- Endpoint discovery staging (auto-repopulates from live traffic)
- The IEEE OUI vendor database (re-synced on a schedule)
- HA cluster-runtime state — the node roster (
cluster_nodes), its join tokens (cluster_join_tokens), and per-node service health (node_service_health). This describes this physical cluster, not portable configuration; the running node keeps its own current roster on restore, so a stale roster can’t resurrect decommissioned or foreign nodes - The schema-version row (the restore target is already at the right schema)
Everything else — your real configuration — is included.
Creating and scheduling backups
Section titled “Creating and scheduling backups”The Backup & Recovery page has three sections: schedule & retention, the archive list, and a recovery-instructions panel. Access is gated by the backup RBAC permission set (view, create, download, delete, manage); if you lack backup.view the page is empty.
The Backup & Recovery page: schedule and retention on top, the archive list with per-archive badges below.
Schedule & retention
Section titled “Schedule & retention”Editable settings (saving requires backup.manage):
| Setting | Default | Meaning |
|---|---|---|
schedule_enabled | off | Master toggle for automatic backups |
interval_hours | 24 | Hours between scheduled backups (floored to a 1-hour minimum) |
retention_count | 5 | How many archives to keep on disk; older ones are pruned. Applies to manual backups too, not only scheduled |
include_certs | on | Pack the internal CA and TLS certificates |
include_daemon_configs | on | Pack the generated TACACS+/RADIUS/NAC config files |
include_host_files | on | Pack the .env and Compose file (the host secrets) |
The database dump is always packed; there is no toggle to exclude it.
When scheduling is enabled, a background worker runs on the configured interval, creates one archive, and prunes to the retention count. A scheduled-backup failure is loud: it records a BACKUP_FAILED audit event and raises a firing alert (cleared automatically on the next successful run). The worker never crashes the scheduler.
Creating a backup now
Section titled “Creating a backup now”Create backup now (backup.create) builds an archive synchronously — it usually takes a few seconds. On success the archive list refreshes.
The archive list
Section titled “The archive list”Each row shows the creation date (in your configured timezone), the filename with its app_version, the size, the trigger badge (manual or scheduled), and “included” badges (db, certs, host_files, daemon_configs). Two actions:
- Download (
backup.download) — streams the.tarthrough the authenticated API; the file never appears in a URL. Store it somewhere safe and off-box. - Delete (
backup.delete) — asks for confirmation with the exact filename, then permanently removes the archive. This cannot be undone.
Restoring (disaster recovery)
Section titled “Restoring (disaster recovery)”Recovery is CLI-only and destructive. A fresh Taranac deploy is not empty — startup migrations and seeds build a working default system — so recovery first wipes the current configuration and then loads the backup. That is why it is a deliberate, off-the-API action.
The intended flow:
- Deploy a fresh, healthy Taranac and log in as the default admin. Confirm the deploy itself works (this separates “deploy failed” from “recovery failed”).
- Have the original master key at hand.
- Run the two phases below.
The recovery panel in the UI documents the same steps; there is no recover-from-browser button by design.
Guided wrapper (recommended)
Section titled “Guided wrapper (recommended)”A helper script runs the in-container steps and prints the by-hand host steps in between:
deploy/taranac-restore.sh prod taranac-backup-YYYYMMDDTHHMMSSZ.tar# or: deploy/taranac-restore.sh dev <archive>.tarPhase 1 — host / secrets
Section titled “Phase 1 — host / secrets”Verify the key against the archive, then decrypt and extract the host secrets and certificates:
docker compose --env-file .env -f docker-compose.prod.yml exec api \ python -m app.scripts.restore host /app/backups/<archive>.tar --out /app/backups/restoreThen put the original .env, Compose file and certificates back and bring the stack up with the original secrets:
cp ./backups/restore/env/env .envcp ./backups/restore/compose/compose.yml docker-compose.prod.ymldocker compose --env-file .env -f docker-compose.prod.yml exec api \ sh -c 'cp -a /app/backups/restore/certs/. /opt/taranac/certs/'docker compose --env-file .env -f docker-compose.prod.yml up -dYou can also pass an explicit key with --master-key <KEY> instead of relying on the running container’s loaded key.
Phase 2 — database
Section titled “Phase 2 — database”Once the stack is healthy again on the restored secrets, restore the configuration:
docker compose --env-file .env -f docker-compose.prod.yml exec api \ python -m app.scripts.restore db /app/backups/<archive>.tarPhase 2 verifies the master key, the version and the schema, asks you to type yes to confirm, then in one transaction truncates the configuration tables and reloads the dump. The HA cluster-runtime tables (cluster_nodes, cluster_join_tokens, node_service_health) are left untouched — the running node keeps its own current cluster membership. An older archive taken before those tables were excluded is filtered on the way in, so a stale roster is never reloaded. Restore then regenerates the TACACS+/RADIUS/NAC daemon configs from the restored database and records a BACKUP_RESTORED audit event. A full stack restart afterwards is recommended so every service re-reads the restored certificates and configuration.
Add --yes to skip the interactive confirmation (for automation).
Hard gates before any destructive action
Section titled “Hard gates before any destructive action”Phase 2 refuses to run unless three checks pass:
| Gate | Check | Override |
|---|---|---|
| Master key | The archive canary decrypts with the loaded key | none — a key mismatch always aborts |
| Version | Archive app_version equals the running version | --force |
| Schema | Archive alembic_head equals the current DB schema revision | --force |
The master-key gate cannot be overridden. The version and schema gates can be bypassed with --force, but only do so when you are certain the configuration is compatible — see upgrades below.
Upgrades & the version model
Section titled “Upgrades & the version model”Taranac ships as a single platform stack: backend, frontend, captive portal and the TACACS+/RADIUS/NAC daemons all roll together under one version and one Git tag. (The optional taranac-mfa push service versions on its own line.) The daemons are downstream consumers of config the backend generates, so they cannot be released separately — what actually guarantees compatibility is the config-format contract between the backend and the daemons, not the version number alone.
How an upgrade runs
Section titled “How an upgrade runs”On every start, the API container applies database migrations automatically (alembic upgrade head) before serving traffic, and regenerates the internal CA/server certificates if they are missing. Upgrading is therefore: pull the new images, recreate the stack, and the container migrates the schema forward on boot.
docker compose --env-file .env -f docker-compose.prod.yml up -d --buildBackup before you upgrade — and why version matters
Section titled “Backup before you upgrade — and why version matters”Because a restore is gated on exact version and schema match, the safe recovery story is: a backup taken on version X restores cleanly onto a deployment running version X. After you upgrade to Y, an older X archive will hit the version/schema gate.
The decision guidance:
| You have… | …and you want to restore onto | Do this |
|---|---|---|
| A backup from the running version | The same version | Restore directly — all gates pass |
| A backup from an older version | A newer deployment | Re-deploy the older version, restore there, then upgrade forward (so migrations run normally); avoid --force |
| A backup whose version you can’t match | A newer deployment | --force overrides the version/schema gate — only if you are confident the schema is compatible; not the default path |
High availability
Section titled “High availability”Taranac ships a turnkey high-availability mode as a licensed Pro feature: the stack runs across two or more hosts with a Patroni-managed primary/replica PostgreSQL cluster (etcd coordination, optional witness for two-node setups), and automatic failover keeps authentication answering when a host goes down. All durable state lives in PostgreSQL, so replicating the database is what makes the system highly available. See High availability for the full architecture, prerequisites, enablement, and day-2 operations, and Licensing for how HA is unlocked.
Backups complement HA — they do not replace it. HA protects against a host or process failure; it does not protect against operator error, a bad upgrade, or corruption that replicates to every node. Whether or not you run HA, keep regular, off-box backups plus a securely stored master key as your recovery-of-last-resort. Note that a configuration backup deliberately excludes the HA cluster-runtime tables (see Configuration vs dynamic data above), so a restore never disturbs live cluster membership — the running node keeps its own roster.
Reference
Section titled “Reference”Recovery CLI
Section titled “Recovery CLI”| Command | Purpose |
|---|---|
python -m app.scripts.restore host <archive> --out <dir> | Phase 1: verify key, extract env/ + compose/ + certs/ |
python -m app.scripts.restore host … --master-key <KEY> | Decrypt with an explicit key instead of the loaded one |
python -m app.scripts.restore db <archive> | Phase 2: verify gates, wipe config tables, reload the dump, regenerate daemon configs |
python -m app.scripts.restore db … --yes | Skip the interactive confirmation |
python -m app.scripts.restore db … --force | Override the version/schema gate (the key gate still applies) |
deploy/taranac-restore.sh <dev|prod> <archive> | Guided wrapper for the whole runbook |
Audit events
Section titled “Audit events”Backups emit BACKUP_CREATED, BACKUP_DELETED, BACKUP_FAILED, BACKUP_SETTINGS_CHANGED and BACKUP_RESTORED — review them in the audit log.
Related
Section titled “Related”- Settings reference — the
backup.*settings registry - Versioning — how platform versions are bumped and what compatibility actually depends on
- Alerts — where a failed scheduled backup surfaces
- Audit & syslog — backup audit events
- Installation — deploying the stack and provisioning the master key
- High availability — the multi-node Pro topology backups complement
- Licensing — how the HA Pro feature is unlocked
- Project repository