On this page
Backup & Restore Runbook
Xodiac state lives in three places: the database (all tenants, checks,
incidents, metrics), writable/ (cache, logs, session files, uploads),
and .env (DB credentials, app key, mail settings). Back all three.
What to back up
| Path | Contents | Backup? |
|---|---|---|
MariaDB xodiac database |
everything tenant-facing | yes — critical |
.env |
DB creds, encryption.key, mail, baseURL |
yes — secrets |
writable/ |
cache, logs, sessions | optional (regenerable; keep logs if needed) |
public/ build |
compiled assets | no — rebuild from frontend/ |
encryption.keyin.envprotects TOTP secrets, SAML keys, channel credentials and webhook secrets at rest. Losing it = losing the ability to decrypt those fields. Store.envin the secrets manager, not just in the backup tarball.
Backup
# 1. Database (consistent snapshot — use --single-transaction for InnoDB)
mysqldump --single-transaction --routines --triggers \
-u xodiac -p xodiac | gzip > xodiac-db-$(date +%F).sql.gz
# 2. Env + writable (small)
tar czf xodiac-files-$(date +%F).tar.gz .env writable/
Schedule nightly via cron/systemd timer; retain 30 daily + 12 monthly. Verify restores quarterly (below).
Restore
# 1. Stop the workers so nothing writes mid-restore
systemctl stop xodiac-poll xodiac-work xodiac-sweep.timer # or: pkill -f 'spark xodiac'
# 2. Database
gunzip -c xodiac-db-YYYY-MM-DD.sql.gz | mysql -u xodiac -p xodiac
# 3. Files
tar xzf xodiac-files-YYYY-MM-DD.tar.gz
# 4. Migrations (schema may be newer than the dump)
php spark migrate
# 5. Verify + restart
php spark xodiac:housekeeping --dry-run 2>/dev/null || true
systemctl start xodiac-poll xodiac-work
Smoke checks after restore:
GET /api/v1/health(or login page) loadsphp spark db:table devicesshows expected row counts- one agent poll succeeds (
tail writable/logs/for agent errors) php spark xodiac:rollup --oncecompletes without error
Tenant-level export/import
For moving a single tenant between instances (or offboarding), use the built-in tenant export — it carries devices, checks, incidents, channels, settings and bounded metrics, and strips generated columns on import:
php spark xodiac:tenant-export <tenant_id> > tenant-<id>.json
php spark xodiac:tenant-import tenant-<id>.json # preview
php spark xodiac:tenant-import tenant-<id>.json --apply # execute
See app/Libraries/TenantMove.php for the exact table set.
Load testing xodiac:poll / xodiac:work
scripts/loadtest.sh times the worker commands and reports table growth.
Seed first (N devices × M checks via the UI enrol flow or API), then:
DB_NAME=xodiac DB_USER=xodiac DB_PASS=... ./scripts/loadtest.sh
It measures --once cycle time for poll/work/rollup/sweep and prints
table row counts so you can verify queue depth, rollup lag, and
notify_log/metric_samples growth rates under load.