# CLI reference ``` nxdns [options] ``` nxdns has six commands: `run`, `check`, `export`, `import`, `version` and `help`. Flags take their value either way, `--data-dir /srv/nxdns` or `--data-dir=/srv/nxdns`. A command rejects a flag it has no use for. ## `run` Serves DNS and the web interface until it receives SIGINT or SIGTERM. | Flag | Meaning | | --- | --- | | `--data-dir DIR` | The data directory, `/var/lib/nxdns` by default. Created at mode 0700 if missing. | | `--config FILE` | Run in file mode, with FILE as the configuration. | | `--admin-dev DIR` | Serve the web interface from DIR instead of the copy built into the binary. For developing the interface. | Without `--config`, nxdns runs in database mode: the configuration is what `config.db` holds, and you change it in the web interface. With `--config FILE`, the file is the configuration: every start makes the database match it, and the web interface shows the configuration read-only. A file at `/etc/nxdns/config.zon` that no `--config` names is not read. [The configuration model](../explanation/configuration-model.md) compares the two. The log names the mode at every start: ``` info(nxdns): authority: database info(nxdns): authority: file (/etc/nxdns/config.zon) ``` In file mode it first says what the file changed: entries added (`+`), changed (`~`) and removed (`-`) per list, and the settings that changed. ``` reconciled '/etc/nxdns/config.zon': upstreams +1 ~0 -0; settings +46 ~0 -0; settings keys changed: dns.bind_ipv4 dns.port web.bind web.port … web authentication is now enabled ``` A start whose file changed nothing says so: ``` reconciled '/etc/nxdns/config.zon': no changes ``` A blocklist whose URL the file still names keeps its download across restarts. Changing a list's URL makes it a new list, downloaded afresh. In file mode, a file that is missing, unreadable or invalid stops the start with exit 2. nxdns never falls back to the database, which would leave you running a configuration you did not deploy: ``` FAIL /etc/nxdns/config.zon: no such file nxdns run failed: ManagedConfigUnreadable run `nxdns check` to see the configuration in full ``` ## `check` Checks a configuration and tests the upstreams, then exits 0 if nothing failed and 2 if something did. It reports every problem, not just the first. | Flag | Meaning | | --- | --- | | `--data-dir DIR` | The data directory whose `config.db` to check. | | `--config FILE` | Check FILE instead of the database. | Like `run`, it checks the file when you name one and the database when you do not, so `nxdns check --config FILE` checks exactly what `nxdns run --config FILE` would serve. Run it before you restart nxdns on a changed file. It checks, in order: 1. That the file or database can be read, and the file's syntax. 2. Every rule in the [configuration reference](configuration.md). 3. For each DoH or DoT listener that is on, that the certificate and key load and belong together. A key that others can read is a warning. 4. One real query for `example.com` through each enabled upstream, within the configured timeouts. This needs network access. ``` checking configuration file /etc/nxdns/config.zon OK upstreams[0] https://cloudflare-dns.com OK: no problems found ``` Each problem is a `FAIL` or a `WARN` line. A `WARN` marks something allowed but probably unintended, such as a blocklist no group uses, and does not change the exit code. The last line sums up: | Found | Last line | Exit | | --- | --- | --- | | Nothing | `OK: no problems found` | 0 | | Warnings only | `OK: no failures found, 2 warnings` | 0 | | A failure | the `FAIL` lines, with no summary | 2 | With no `--config` and no database yet, `check` says how to get one: ``` no config database at /var/lib/nxdns/config.db load one with `nxdns import `, or make a file the source of truth with `nxdns run --config ` ``` `check` never changes the database. Two of its findings are therefore not damage: - `FAIL : schema version , this nxdns expects ; …`: the database is from an older release. Starting nxdns upgrades it. - `FAIL : uncheckpointed changes are waiting in -wal, …`: recent changes from a running nxdns are not in the main file yet. Check an export with `--config` instead, or stop nxdns first. ## `export` Writes the configuration to standard output as ZON, or to a file with `--out`. | Flag | Meaning | | --- | --- | | `--data-dir DIR` | The data directory holding `config.db`. | | `--out FILE` | Write to FILE, created at mode 0600, instead of standard output. | The output lists every setting, defaults included, in a fixed order with no timestamps, so exporting the same configuration twice gives the same bytes. It works while nxdns is running. The password is written as `.password = null` with its hash in `.password_hash`, so the export restores the same password without revealing it. See [Back up and restore](../how-to/back-up-and-restore.md). ## `import FILE` Makes the database match FILE, all at once or not at all. If FILE has any problem, `import` reports every one and changes nothing. It creates the data directory at mode 0700 if it is missing. | Flag | Meaning | | --- | --- | | `--data-dir DIR` | The data directory holding `config.db`. | | `--allow-delete` | Apply a file that removes entries the database holds. | Stop nxdns before you import: a running server would pick up only part of the change. ### Removing entries Entries the database holds and FILE does not name are removed. That is how a restore undoes later changes, and also how importing the wrong file would empty a configured server, so `import` refuses until you add `--allow-delete`: ``` FAIL import: this file would delete rows the database holds (upstreams 1); re-run with --allow-delete to apply it import failed: DestructiveImport ``` The message says how many entries of each kind would go. Nothing is changed, and the exit code is 2. Adding entries or changing them needs no flag. An entry is recognised by these fields, and changing one of them removes the old entry and adds a new one: | List | Recognised by | | --- | --- | | `upstreams`, `blocklist_sources` | `url` | | `groups` | `name` | | `clients` | `ip` | | `client_prefixes` | `prefix` | | `forward_zones` | `zone` | | `local_records` | `name`, `rtype` and `value` | | `rules` | `group`, `pattern`, `kind` and `action` | Renaming a group or correcting an upstream's URL therefore needs `--allow-delete`; changing a client's group or a list's name does not. ### What an import keeps A blocklist whose URL is unchanged keeps its download. Devices nxdns discovered keep their history, and a device the file names keeps its first-seen and last-seen times. A discovered device whose group the file removes moves to `default`. ## `version` Prints the nxdns version, then the Zig version it was built with. ``` nxdns zig 0.16.0 ``` ## `help` Prints the usage text and exits 0. `nxdns --help` and `nxdns -h` do the same. ## Exit codes | Code | Meaning | | --- | --- | | 0 | Success. | | 1 | A failure a retry might clear: a disk, network or database error, or running out of memory. | | 2 | Something only you can fix: a configuration problem, a `check` that found one, a `--config` file that is missing or unreadable, an `import` that would remove entries, or a database nxdns refuses to open. | | 64 | A wrong command line: an unknown command or flag, or a missing or extra argument. | The packaged systemd unit restarts nxdns after exit 1 and not after exit 2 or 64, since restarting would only repeat the error. When `run` exits 2 it says what to do next: ``` run `nxdns check` to see the configuration in full ``` `run` also exits 2 when it refuses the query history database, `querylog.db`, rather than risk it. The message names the file and what to do. A database from a newer release, after you went back to an older one, is the usual case; see [Going back to an older release](../how-to/upgrade.md#going-back-to-an-older-release).