Files
nxdns/docs/reference/cli.md
T

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:

  1. Which source to check (see source selection).
  2. Full validation — the same rules import enforces.
  3. 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).
  4. A live probe: one real A query for example.com through every enabled upstream, driving the same pool and failover machinery the server uses, with upstream.total_timeout_ms as the per-attempt deadline. A FAIL line names the upstream and the concrete cause recorded in its health. This probe leaves the machine, so check needs 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 FILE given explicitly: check that file, nothing else.
  • Otherwise, if <data-dir>/config.db exists: check the database — the right default, since the database is the truth on a configured server. Pending schema migrations are applied first, so a check immediately 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 exportimportexport 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.