215 lines
7.3 KiB
Markdown
215 lines
7.3 KiB
Markdown
# 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.
|