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.key in .env protects TOTP secrets, SAML keys, channel credentials and webhook secrets at rest. Losing it = losing the ability to decrypt those fields. Store .env in 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) loads
  • php spark db:table devices shows expected row counts
  • one agent poll succeeds (tail writable/logs/ for agent errors)
  • php spark xodiac:rollup --once completes 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.