6.7 KiB
CLI reference
nxdns <command> [options]
Six subcommands: run, check, export, import, version, help. Source
of truth: src/cli.zig.
Every flag takes both spellings, --flag value and --flag=value. An attached
value that is empty (--config=) is a missing value, not an empty path.
--force is boolean and takes no value at all, so --force=1 is not a spelling
of any flag this program has. A flag is rejected by the subcommand that has no
use for it: --web-dev outside run is an unknown flag, not a no-op.
run
Serves DNS until SIGINT or SIGTERM.
| Flag | Meaning |
|---|---|
--data-dir DIR |
Data directory (default /var/lib/nxdns). Created at mode 0700 if missing. |
--config FILE |
Seed configuration file (default /etc/nxdns/config.zon). Read only when the database has never been configured. |
--web-dev DIR |
Serve the web interface from DIR instead of the embedded assets, with no cache headers. Development only. |
Seeding failures are not treated as configuration faults here: a seed file that is unparseable, oversized or invalid prints its diagnostics and exits 1, not 2. See exit codes.
check
Validates the configuration and probes the upstreams. Exit 0 when clean, 2 when it found problems. It always reports every problem, not just the first. What it checks, in order:
- Which source to check (see source selection).
- Full validation — the same rules
importenforces. - For each enabled DoH/DoT listener: certificate and key are readable (FAIL if not), and the key's permissions are owner-only (WARN if any group or other bit is set; the WARN does not change the exit code).
- A live probe: one real A query for
example.comthrough every enabled upstream, driving the same pool and failover machinery the server uses, withupstream.total_timeout_msas the per-attempt deadline. A FAIL line names the upstream and the concrete cause recorded in its health. This probe leaves the machine, socheckneeds network access to pass. It runs from the command line but not from unit tests.
| Flag | Meaning |
|---|---|
--data-dir DIR |
Data directory to look for config.db in. |
--config FILE |
Check this file instead of the database. |
Source selection
--config FILEgiven explicitly: check that file, nothing else.- Otherwise, if
<data-dir>/config.dbexists: check the database — the right default, since the database is the truth on a configured server. Pending schema migrations are applied first, so acheckimmediately after an upgrade works. - Otherwise, if the default config file path exists: check it.
- Otherwise: "nothing to check", exit 2.
The first line of output always names which source was checked. A file larger
than 4 MiB fails with larger than 4194304 bytes; a ZON syntax error is
reported with its line and column.
A missing file is treated differently depending on how the path was reached. An
explicit --config FILE that does not exist is an I/O failure: check failed: FileNotFound on stderr, exit 1. A default path that does not exist is one of
the cases above and produces "nothing to check", exit 2.
export
Writes the configuration as ZON to stdout, or atomically at mode 0600 to
--out FILE. --out paths are relative to the shell's working directory, not
to the data directory. The output is canonical: every default emitted,
deterministic ordering, no timestamps, so export → import → export is
byte-identical.
export does not create the data directory; it fails if the directory is not
there. A directory that exists without a config.db is not that case: the
database is opened with create semantics, so an empty config.db is created and
migrated, and the export is of a default configuration.
| Flag | Meaning |
|---|---|
--data-dir DIR |
Data directory holding config.db. |
--out FILE |
Write to FILE instead of stdout. |
See back up and restore.
import FILE
Validates FILE and replaces the whole configuration with it in one transaction.
Prints every validation problem on failure; a failed import leaves the database
untouched. Refuses a database that already has content unless --force is
given (DatabaseNotEmpty, exit 2). Creates the data directory at mode 0700 if
it is missing.
FILE is positional and may appear before or after the flags.
| Flag | Meaning |
|---|---|
--data-dir DIR |
Data directory holding config.db (created if missing). |
--force |
Replace a database that already has content. |
version
Prints two lines: the nxdns version with the git commit, then the Zig version the binary was built with. Takes no flags and no arguments.
help
Prints the usage text to stdout and exits 0. nxdns --help and nxdns -h do
the same. A usage error prints the same text to stderr and exits 64.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success. |
| 1 | Runtime failure — I/O, database, out of memory. A partial diagnostic report caused by an allocation failure is a runtime failure, not a verdict on the configuration. |
| 2 | A configuration problem the operator can fix, or a check that found one. Which faults qualify differs per subcommand; see below. |
| 64 | Usage error — unknown command or flag, a flag without its value, a missing or extra argument. |
Code 2 is not a single rule shared by every subcommand. check and import
classify configuration faults; run maps only four errors to it and lets
everything else out as a runtime failure.
check exits 2 when validation reported at least one problem, when a
certificate or key named by an enabled listener is unreadable, when a probed
upstream failed, when the file is larger than 4 MiB, when the file has a ZON
syntax error, and when there was nothing to check. Any other error escaping the
run — a missing explicit --config file, an unreadable database — prints
check failed: <Error> and exits 1.
import exits 2 when validation recorded at least one problem, and for
DatabaseNotEmpty, ConfigTooLarge, ParseZon and PasswordAndHashBothSet.
OutOfMemory is 1 even when problems were recorded, because the report is then
incomplete. Every other error is 1.
run exits 2 for NoUsableUpstreams, BadBindAddress, BadRateLimit and
BadCertificate, and points the operator at nxdns check on stderr when it
does. Nothing else is remapped. In particular, seeding the database from
--config on first start happens before that classification applies to it: a
seed file that is unparseable (ParseZon), larger than 4 MiB
(ConfigTooLarge), or rejected by validation prints its diagnostics to stderr
and exits 1. The same file given to check or import exits 2.
Where an exit code sends you next: troubleshoot.