8.9 KiB
Back up and restore
The configuration is the only state worth keeping. nxdns export writes it out
as a ZON file and nxdns import writes one back. The query log is deliberately
not part of a backup: it is expendable history, and if it is missing it gets
recreated empty.
Which authority the service runs under decides what the backup is. Check the start log:
authority: database—config.dbholds the configuration. Back it up withnxdns export, and restore withnxdns importor by replacing the database file.authority: file (<path>)— that file holds the configuration, and it is already a text file you can keep in git. The file is the backup. Restoring means putting the file back and restarting; the database rebuilds itself from it.config.dbis a cache of the file in this mode, not the thing to preserve.
The rest of this page covers database mode unless it says otherwise.
The commands below use the scratch lab from
enable DoH and DoT, data directory
/tmp/nxdns-lab/data. On a real install drop --data-dir and the default
/var/lib/nxdns applies.
Back up
One command, against a stopped or a running server:
nxdns export --data-dir /tmp/nxdns-lab/data --out /tmp/nxdns-lab/backup.zon
wrote /tmp/nxdns-lab/backup.zon
The write is a temp file plus a rename, so an interrupted export leaves no half-written backup, and the result is mode 0600:
stat -c '%a %n' /tmp/nxdns-lab/backup.zon
600 /tmp/nxdns-lab/backup.zon
That mode is not decoration. The file carries web.password_hash:
grep password /tmp/nxdns-lab/backup.zon
.password = null,
.password_hash = "$argon2id$v=19$m=19456,t=2,p=1$hOjjnTrTZ6kU8XrwQuJ7ZFC3B5LumaB4hRe7kBbzJ6Q$YsC2rCTDKv96eEwPhs+D6vbDliLogEZph8EkagKSuy8",
Treat backups as secrets. .password is always exported as null — the
plaintext is never stored anywhere — so the file re-imports without anyone
knowing the password. null and "" are different statements here: null
means the file says nothing about the password, while an empty password_hash
would disable authentication. See
Password and hash.
Without --out the export goes to stdout, where the file mode is your
redirect's problem:
nxdns export --data-dir /tmp/nxdns-lab/data | head -10
// nxdns configuration
// generated by `nxdns export` from the running configuration
.{
.upstream = .{
.attempt_timeout_ms = 2500,
.read_timeout_ms = 3000,
.total_timeout_ms = 5000,
},
.dns = .{
.bind_ipv4 = "127.0.0.1",
Runtime facts are left out on purpose: client first-seen and last-seen times, rule creation times, per-source domain counts and checksums. They are things a running server produces, not configuration, and including them would make two exports taken minutes apart differ.
Restore onto a fresh data directory
This is the normal restore: new machine, new disk, empty data directory. Nothing to overwrite, so nothing to authorise.
nxdns import /tmp/nxdns-lab/backup.zon --data-dir /tmp/nxdns-lab/data-restored
info(migrations): config.db migrated from schema version 0 to 2
imported /tmp/nxdns-lab/backup.zon
The migration line is expected: import creates and migrates the database
before writing to it.
Restore over an existing database
Stop the server first. import rewrites configuration underneath a process
that read it at startup, and a running server picks up only part of it —
filtering follows the new rows at the next reload, while upstreams, listeners
and settings stay at their boot values until a restart.
systemctl stop nxdns
nxdns import /var/backups/nxdns-config.zon
systemctl start nxdns
import converges the database onto the file. Rows the file still names are
matched and updated in place; rows it no longer names are deleted. That last
part is what a restore of an old backup does to everything added since, so it
takes a flag:
nxdns import /tmp/nxdns-lab/old-backup.zon --data-dir /tmp/nxdns-lab/data
FAIL import: this file would delete rows the database holds (upstreams 1); re-run with --allow-delete to apply it
import failed: DestructiveImport
That exits 2 and rolls the transaction back, so nothing is half-applied. The
message names every table that would lose rows, which is usually enough to tell
an intended restore from the wrong file. Say --allow-delete when deleting is
what you mean:
nxdns import /tmp/nxdns-lab/old-backup.zon --allow-delete --data-dir /tmp/nxdns-lab/data
imported /tmp/nxdns-lab/old-backup.zon
A restore that only puts back what is already there needs no flag at all, and neither does one that only adds rows. The flag is about deletion specifically — including deletion in disguise: renaming a group, or correcting a typo in an upstream URL, changes the row's identity, so the engine sees one row gone and one arrived.
What survives a restore is worth knowing before you take an old backup out of the drawer. Blocklist download state is kept for every source whose URL the file still names — checksum, counters, compiled files, and the row id they are keyed by — so restoring does not cost a re-download. Devices the server discovered from traffic are kept whole, and one the backup names keeps the first-seen and last-seen the database already held, so a restore never restamps your network as newly arrived. What does go is a client the backup does not name and that was named by hand: that row is declarative, and it is deleted with the rest.
The three
systemctllines are the only commands on this page that were not run: this machine has no installed nxdns unit (systemctl status nxdnsanswersUnit nxdns.service could not be found.) andsystemctl stop/startneed root. Thenxdns importbetween them is the same command the lab blocks above run, which were executed here as written.
Restoring the database file itself
Copying config.db back into place works too, and it is the fastest restore on
a machine that still has one. Two rules, and the second one is where restores go
wrong:
- Take the copy from a stopped instance, or use
nxdns exportinstead. A copy taken while nxdns is running catches the main file without the changes sitting in its write-ahead log. - Delete any stale
config.db-walandconfig.db-shmbeside the file you restore. SQLite silently discards a write-ahead log that does not match the database it sits next to. It does not warn, and it does not fail — it just answers from the main file, so a restore quietly loses its own tail.
systemctl stop nxdns
rm -f /var/lib/nxdns/config.db-wal /var/lib/nxdns/config.db-shm
cp /var/backups/config.db /var/lib/nxdns/config.db
chown nxdns:nxdns /var/lib/nxdns/config.db
chmod 0600 /var/lib/nxdns/config.db
systemctl start nxdns
Not verified on this host: these need root, an installed unit and
/var/lib/nxdns, none of which exist here.
None of this applies in file mode. There config.db is derived state — restore
the configuration file and start the service, and the first reconcile rebuilds
the database from it.
Verify a backup
The round trip is byte-stable: exporting, importing and exporting again gives an identical file. That is the cheapest check that a backup is complete and that it will load.
nxdns export --data-dir /tmp/nxdns-lab/data --out /tmp/nxdns-lab/backup2.zon
diff /tmp/nxdns-lab/backup.zon /tmp/nxdns-lab/backup2.zon \
&& echo "round trip is byte-identical"
wrote /tmp/nxdns-lab/backup2.zon
round trip is byte-identical
The same check works across data directories — export from the restored copy and diff against the backup you restored from:
nxdns export --data-dir /tmp/nxdns-lab/data-restored --out /tmp/nxdns-lab/backup3.zon
diff /tmp/nxdns-lab/backup.zon /tmp/nxdns-lab/backup3.zon \
&& echo "restored database exports identically"
wrote /tmp/nxdns-lab/backup3.zon
restored database exports identically
What is not in the backup
Everything under the data directory other than config.db:
querylog.db— expendable history, recreated empty when absent.blocklists/— compiled snapshots. They are rebuilt from the sources named in the configuration, so restoring the configuration is enough; the first refresh after a restore downloads them again.
The data directory layout is in files and directories.
A backup before every upgrade
There is no downgrade path. Schema migrations run forward automatically at
startup and before export and import; nothing walks them back, and nxdns check does not run them at all. Take an export before installing a new binary —
see upgrade.
Every nxdns command on this page was executed on this host as written. The
systemctl, cp, chown and chmod lines were not: they need root and an
installed unit, and both blocks holding them say so.