7.3 KiB
Upgrade nxdns
Replaces a running nxdns with a newer build without losing its configuration. The database is migrated in place on the first start of the new binary.
Verification: the export, the migration behaviour and the
version/checksteps below were run on the machine that wrote this page, against a populated scratch data directory and with an explicit--data-dir, since that machine has no/var/lib/nxdns. The commands were not run exactly as printed — the page uses the defaults and placeholders a real operator would have (/var/lib/nxdns,/some/backup, atargethost), and every block where the substitution matters, or which was not run at all, carries its own note. Nothing here was verified except where a note says so.
1. Take an export first
There is no downgrade path, so the export is what you fall back to:
nxdns export --out /some/backup/nxdns-config.zon
/some/backup is a stand-in for a directory you keep backups in, and the
command relies on the default --data-dir /var/lib/nxdns that a systemd
install has.
Verified on this host with both paths substituted, since it has neither
/var/lib/nxdnsnor/some/backup.SCRATCHbelow is a scratch directory, and itsdata/was populated beforehand withnxdns import:$ nxdns export --data-dir $SCRATCH/data --out $SCRATCH/nxdns-config.zon wrote /…/scratchpad/nxdns-config.zon $ stat -c '%a %n' $SCRATCH/nxdns-config.zon 600 /…/scratchpad/nxdns-config.zon $ grep password $SCRATCH/nxdns-config.zon .password = "", .password_hash = "$argon2id$v=19$m=19456,t=2,p=1$mCNEo…$i3DMz…",The shell umask was 022, so the 0600 is
exportsetting it, not the umask. Only the two paths differ from the command above.
The file is written atomically at mode 0600 and carries
web.password_hash, so treat it as a secret. See
Back up and restore for the full backup story. The
query log is deliberately not part of it.
2. Build the new binary
(cd web && npm ci && npm run build)
zig build cross -Dweb-dist=web/dist -Doptimize=ReleaseSafe
Rebuild web/dist before the binary on every upgrade. The admin interface is
embedded at build time, and an old bundle against a new API is a broken
settings page.
3. Replace the binary
systemd
Step 2 leaves the new binary under zig-out/cross, one per target. Copy the
one that matches the host — aarch64-linux-musl for a Raspberry Pi 5:
scp zig-out/cross/x86_64-linux-musl/nxdns target:/tmp/nxdns
Not run on this host:
targetis a placeholder for the machine running nxdns, and this host has no such second machine to copy to. What exists here is the local half —zig build crossproducedzig-out/cross/x86_64-linux-musl/nxdns.
Then, as root on the target:
install -m 0755 /tmp/nxdns /usr/local/bin/nxdns
systemctl restart nxdns
journalctl -u nxdns -f
Not run on this host: all three lines need root and an installed service. The migration half of what a restart does is checkable without either, and was — see the note under What happens to the database. The swap of an older binary for a newer one on a live service was not reproduced here.
Docker
cd deploy/docker
docker compose build
docker compose up -d
Compose recreates the container against the same nxdns-data volume. The seed
file in etc-nxdns is not read again; the database in the volume is the
configuration.
Verified on this host for the first two lines:
docker compose config -qexited 0, anddocker compose buildfinished withImage nxdns Built.docker compose up -dwas not run — it publishes host ports 53/udp, 53/tcp and 8080, which this workstation is not a deploy target for.
4. Confirm the upgrade
nxdns version
nxdns check
dig @127.0.0.1 example.com A +short
nxdns check with no --config checks the database, which is what you want
after an upgrade — it names its source on the first line and migrates a
database that is one schema version behind before checking it:
checking database /var/lib/nxdns/config.db
OK https://cloudflare-dns.com/dns-query
OK: no problems found
Verified on this host for the first two commands, with
--data-dirpointing at the scratch data directory instead of/var/lib/nxdns:$ nxdns version nxdns 0.1.0-dev (unknown) zig 0.16.0 $ nxdns check --data-dir $SCRATCH/data checking database /…/scratchpad/data/config.db OK https://cloudflare-dns.com/dns-query OK: no problems foundThe database path on the first line is the only difference from the block above. The
digline was not run in this round: nothing is listening on 127.0.0.1:53 here, and port 53 needs root.
What happens to the database
Migrations run at startup, and also before check, export and import, so
whichever of those you run first performs the upgrade. A fresh database is
created at the current schema version; an older one is stepped up to it. The
log line names both versions:
info(migrations): config.db migrated from schema version 0 to 2
Verified on this host: that exact line is what
nxdns importprinted when it created the scratch database used throughout this page. An empty data directory is schema version 0, which is why a first run reports a migration rather than nothing. The step from a populated older schema to 2 was not reproduced here — it needs a database written by an older binary, which this host does not have.
Rolling back is the case that has no answer. A database stamped by a newer binary refuses to open, so an older binary against an upgraded data directory fails to start:
warning(migrations): config.db is at schema version 99; this nxdns binary supports 2
nxdns run failed: SchemaTooNew
Not reproduced on this host: the same missing ingredient as above, a database at a schema version this binary does not support. The two lines are the messages
src/storage/migrations.zigemits, not a run captured here.
That run exits 1. Recovering means importing the export you took in step 1 into a fresh data directory with the older binary.
Changing settings, not the binary
An upgrade never re-reads /etc/nxdns/config.zon. After the first successful
seed the file is ignored, and the start log says so:
info(config_bootstrap): configuration file ignored; the database is already configured
Change settings through the admin interface, through the API, or with an export–edit–import cycle against a stopped server:
nxdns export --out config-backup.zon
$EDITOR config-backup.zon
systemctl stop nxdns
nxdns import config-backup.zon --force
systemctl start nxdns
--force is required here. A plain import into a database that already has
content fails with import failed: DatabaseNotEmpty and exits 2, so it cannot
clobber a configured server by accident.
Verified on this host for the two
nxdnslines, against a populated scratch data directory:$ nxdns import $SCRATCH/nxdns-config.zon --data-dir $SCRATCH/data import failed: DatabaseNotEmpty (exit 2) $ nxdns import $SCRATCH/nxdns-config.zon --data-dir $SCRATCH/data --force imported /…/scratchpad/nxdns-config.zon (exit 0)The
systemctl stop/startlines around them need root and an installed service and were not run;$EDITORis yours to run.