# 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 commands below use the scratch lab from [enable DoH and DoT](enable-doh-and-dot.md), 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: ```sh 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: ```sh 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`: ```sh 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", ``` Treat backups as secrets. `.password` is always exported as `""` — the plaintext is never stored anywhere — so the file re-imports without anyone knowing the password. Without `--out` the export goes to stdout, where the file mode is your redirect's problem: ```sh nxdns export --data-dir /tmp/nxdns-lab/data | head -6 ``` ``` // nxdns configuration // generated by `nxdns export` — the database is the source of truth .{ .upstream = .{ .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. No `--force`, because there is nothing to overwrite. ```sh 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 `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: ```sh systemctl stop nxdns nxdns import /var/backups/nxdns-config.zon --force 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: ```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 ``` ``` wrote /tmp/nxdns-lab/pre-restore.zon imported /tmp/nxdns-lab/pre-restore.zon ``` ## 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. ```sh 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: ```sh 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](../reference/files-and-directories.md). ## 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](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.