milestone 20: declarative configuration for iac
Gates / test-aarch64 (push) Successful in 6m45s
Gates / frontend (push) Successful in 51s
Gates / test (push) Successful in 1m37s
Gates / container (push) Failing after 7m31s
Gates / package (push) Failing after 15m14s
CI / gates (push) Failing after 24m27s
Gates / test-aarch64 (push) Successful in 6m45s
Gates / frontend (push) Successful in 51s
Gates / test (push) Successful in 1m37s
Gates / container (push) Failing after 7m31s
Gates / package (push) Failing after 15m14s
CI / gates (push) Failing after 24m27s
This commit is contained in:
@@ -1,9 +1,22 @@
|
||||
# Back up and restore
|
||||
|
||||
The configuration database 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.
|
||||
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.db` holds the configuration. Back it up with
|
||||
`nxdns export`, and restore with `nxdns import` or 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.db` is 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](enable-doh-and-dot.md), data directory
|
||||
@@ -40,13 +53,16 @@ grep password /tmp/nxdns-lab/backup.zon
|
||||
```
|
||||
|
||||
```
|
||||
.password = "",
|
||||
.password_hash = "$argon2id$v=19$m=19456,t=2,p=1$Gh+zg9xke6BqSVOiouRbqG50+Bs8ZGcXA6oKgs7lrKg$crTNMu5OI8yKBkp31r4+Y1OUQmLiAlH/qvsIxjBQRq4",
|
||||
.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 `""` — the
|
||||
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.
|
||||
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](../reference/configuration.md#password-and-hash).
|
||||
|
||||
Without `--out` the export goes to stdout, where the file mode is your
|
||||
redirect's problem:
|
||||
@@ -57,7 +73,7 @@ nxdns export --data-dir /tmp/nxdns-lab/data | head -10
|
||||
|
||||
```
|
||||
// nxdns configuration
|
||||
// generated by `nxdns export` — the database is the source of truth
|
||||
// generated by `nxdns export` from the running configuration
|
||||
.{
|
||||
.upstream = .{
|
||||
.attempt_timeout_ms = 2500,
|
||||
@@ -75,8 +91,8 @@ exports taken minutes apart differ.
|
||||
|
||||
## Restore onto a fresh data directory
|
||||
|
||||
This is the normal restore: new machine, new disk, empty data directory. No
|
||||
`--force`, because there is nothing to overwrite.
|
||||
This is the normal restore: new machine, new disk, empty data directory.
|
||||
Nothing to overwrite, so nothing to authorise.
|
||||
|
||||
```sh
|
||||
nxdns import /tmp/nxdns-lab/backup.zon --data-dir /tmp/nxdns-lab/data-restored
|
||||
@@ -92,64 +108,95 @@ before writing to it.
|
||||
|
||||
## Restore over an existing database
|
||||
|
||||
`import` refuses a database that already holds configuration, so a plain
|
||||
`import` can never clobber a configured server by accident:
|
||||
|
||||
```sh
|
||||
nxdns import /tmp/nxdns-lab/backup.zon --data-dir /tmp/nxdns-lab/data
|
||||
```
|
||||
|
||||
```
|
||||
import failed: DatabaseNotEmpty
|
||||
```
|
||||
|
||||
That exits 2. Say `--force` when replacing is what you mean:
|
||||
|
||||
```sh
|
||||
nxdns import /tmp/nxdns-lab/backup.zon --force --data-dir /tmp/nxdns-lab/data
|
||||
```
|
||||
|
||||
```
|
||||
imported /tmp/nxdns-lab/backup.zon
|
||||
```
|
||||
|
||||
Stop the server first. `import` replaces the whole configuration underneath a
|
||||
process that has already read it, and a running server will not notice.
|
||||
|
||||
What `--force` does to the client list is worth knowing before you restore an
|
||||
old backup. A client the backup does not name is removed, and its first-seen
|
||||
and last-seen go with it — restoring a month-old file drops the devices you
|
||||
named since. Devices the server discovered from traffic are kept, and a device
|
||||
the backup does name keeps the first-seen and last-seen the database already
|
||||
held, so a restore does not restamp your whole network as newly arrived.
|
||||
|
||||
On a real install that is the systemd unit:
|
||||
**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.
|
||||
|
||||
```sh
|
||||
systemctl stop nxdns
|
||||
nxdns import /var/backups/nxdns-config.zon --force
|
||||
nxdns import /var/backups/nxdns-config.zon
|
||||
systemctl start nxdns
|
||||
```
|
||||
|
||||
**Not verified on this host.** Those three lines are the only commands on this
|
||||
page that were not run: this machine has no installed nxdns systemd unit
|
||||
(`systemctl status nxdns` answers `Unit nxdns.service could not be found.`) and
|
||||
`systemctl stop`/`start` need root. The lab equivalent below was run, and it
|
||||
exercises the same stop-import-start sequence. In the lab the server is a
|
||||
foreground `nxdns run`, so stopping it is Ctrl-C in its own terminal:
|
||||
`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:
|
||||
|
||||
```sh
|
||||
nxdns export --data-dir /tmp/nxdns-lab/data --out /tmp/nxdns-lab/pre-restore.zon
|
||||
# Ctrl-C the `nxdns run` terminal, or `kill` its pid from another shell
|
||||
nxdns import /tmp/nxdns-lab/pre-restore.zon --force --data-dir /tmp/nxdns-lab/data
|
||||
nxdns run --data-dir /tmp/nxdns-lab/data --config /tmp/nxdns-lab/etc/config.zon
|
||||
nxdns import /tmp/nxdns-lab/old-backup.zon --data-dir /tmp/nxdns-lab/data
|
||||
```
|
||||
|
||||
```
|
||||
wrote /tmp/nxdns-lab/pre-restore.zon
|
||||
imported /tmp/nxdns-lab/pre-restore.zon
|
||||
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:
|
||||
|
||||
```sh
|
||||
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 `systemctl` lines are the only commands on this page that were not
|
||||
> run: this machine has no installed nxdns unit (`systemctl status nxdns`
|
||||
> answers `Unit nxdns.service could not be found.`) and `systemctl stop`/`start`
|
||||
> need root. The `nxdns import` between 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:
|
||||
|
||||
1. **Take the copy from a stopped instance**, or use `nxdns export` instead. A
|
||||
copy taken while nxdns is running catches the main file without the changes
|
||||
sitting in its write-ahead log.
|
||||
2. **Delete any stale `config.db-wal` and `config.db-shm` beside 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.
|
||||
|
||||
```sh
|
||||
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
|
||||
@@ -200,5 +247,6 @@ 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](upgrade.md).
|
||||
|
||||
Every command on this page was executed on this host as written, except the
|
||||
`systemctl` block marked **Not verified on this host** above.
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user