An operator runbook for backing up an unmask install and restoring it on a new host.
The paths below are the defaults — check your /etc/unmask/config.yml for the
actual values.
What to back up (and why)
Item
Path (default)
Why it matters
config.yml
/etc/unmask/config.yml
Holds bv_secret + captcha_secret_base and the DB connection. Losing or changing bv_secret invalidates every issued _bv cookie, so all current visitors are re-challenged at once (no outage, but a challenge spike). DB credentials live here too.
database
SQLite: /var/lib/unmask/unmask.sqlite (+ -wal / -shm) MariaDB: the unmask_* schema
nginx reads it directly to enforce file-based bans; not reconstructable from the DB alone if it was edited out-of-band.
You do not need to back these up — they are regenerated automatically:
/var/lib/unmask/nginx/*.inc (unmask render-nginx rebuilds them),
community-bans-*.map (re-pulled from the feed), and the placed
ngx_http_unmask_module.so (re-placed on reinstall / nginx start).
Backup
# 1. config + ban file (cheap โ do this often)
install -d -m 0700 /backup/unmask
cp -a /etc/unmask/config.yml /backup/unmask/
cp -a "$(awk '/ban_file:/{print $2}' /etc/unmask/config.yml)" /backup/unmask/ 2>/dev/null || true
# 2a. SQLite โ consistent snapshot WITHOUT stopping the daemon
sqlite3 /var/lib/unmask/unmask.sqlite ".backup '/backup/unmask/unmask.sqlite'"
# 2b. MariaDB
mysqldump --single-transaction --routines unmask_<db> > /backup/unmask/unmask.sql
--single-transaction (InnoDB) gives a consistent dump without locking. The SQLite
.backup command is safe against a live writer — it copies pages under a read lock,
whereas a plain cp of a live SQLite file can capture a torn WAL.
Restore
Install the same unmask version that produced the backup (unmask version).
Restore config.ymlfirst — this preserves bv_secret, so already-issued _bv cookies keep validating and visitors are not mass-re-challenged.
Restore the database:
SQLite: stop the daemon, copy the file back, start.
MariaDB: mysql unmask_<db> < /backup/unmask/unmask.sql.
Start / restart the admin service; confirm with unmask doctor and curl -sf http://127.0.0.1:9477/unmask/healthz.
Switching the database driver (SQLite ⇄ MariaDB)
unmask ships no built-in cross-driver data migration. Two options:
Clean switch (recommended when stats history is not precious)
Point db.driver (+ the MariaDB connection) in config.yml at the new driver,
run unmask migrate to create the schema there, and restart. The new database starts
empty — historical events / stats are left behind, and admin users + bans must be
re-created (or imported manually). Easiest done early, before stats accumulate. The admin
reconfigure wizard (/admin/setup/ on a configured install) does the same with a UI.
Preserve data (manual)
Dump the old driver, hand-translate the SQL dialect differences (SQLite ↔ MariaDB types /
quoting), and load into the schema created by unmask migrate. This is version-specific
and unsupported as a one-command path; verify row counts afterward.
Upgrades & verifying
unmask migrate (run by the daemon on start, or manually) is idempotent and
re-runnable, which is what makes it safe on MariaDB where DDL is non-transactional
(each ALTER / CREATE auto-commits and cannot roll back). Every column / table
migration is check-then-apply — hasColumn / hasTable is consulted
before each ALTER, and the baseline is CREATE TABLE IF NOT EXISTS. A migration
interrupted part-way is recovered by simply re-running unmask migrate: the already-applied
steps are detected and skipped.
Note (MariaDB only). The one-time unmask_cookie_minute schema-v2 copy that
runs on the first start after an upgrade is not transaction-wrapped. If it is interrupted mid-copy
and then re-run, it can double-count some cookie-minute stats rows — inflated stats
only, with no data loss and no outage. Letting that first post-upgrade start finish uninterrupted
avoids it. SQLite is unaffected (its migrations run in a single transaction and roll back atomically).
Verify
unmask doctor # DB ping + schema + config checks
unmask version # confirm the restored binary version