# 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`/`check` > steps 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`, a `target` host), 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: ```sh 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/nxdns` nor `/some/backup`. `SCRATCH` below is a scratch directory, > and its `data/` was populated beforehand with `nxdns 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 `export` setting 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](back-up-and-restore.md) for the full backup story. The query log is deliberately not part of it. ## 2. Build the new binary ```sh (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: ```sh scp zig-out/cross/x86_64-linux-musl/nxdns target:/tmp/nxdns ``` > Not run on this host: `target` is 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 cross` produced > `zig-out/cross/x86_64-linux-musl/nxdns`. Then, as root on the target: ```sh 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](#what-happens-to-the-database). The swap of > an older binary for a newer one on a live service was not reproduced here. ### Docker ```sh 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 -q` > exited 0, and `docker compose build` finished with `Image nxdns Built`. > `docker compose up -d` was 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 ```sh 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-dir` pointing > 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 found > ``` > > The database path on the first line is the only difference from the block > above. The `dig` line 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 import` printed 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.zig` emits, 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: ```sh 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 `nxdns` lines, 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`/`start` lines around them need root and an installed > service and were not run; `$EDITOR` is yours to run.