Install with systemd
Install nxdns as a system service on a Linux machine with systemd, including a Raspberry Pi. At the end it answers DNS on port 53 for your network and starts on boot.
1. Download and verify
Each release has two builds: x86_64-linux-musl for a PC or server, and aarch64-linux-musl for a Raspberry Pi or another 64-bit ARM machine. Both are static binaries that need nothing installed. On the machine that will run nxdns:
mkdir -p ~/nxdns-release && cd ~/nxdns-releasecurl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v0.0.25/nxdns-0.0.25-x86_64-linux-musl.tar.gzcurl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v0.0.25/SHA256SUMS.txtcurl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v0.0.25/SHA256SUMS.txt.ascgpg --verify SHA256SUMS.txt.asc SHA256SUMS.txtsha256sum -c --ignore-missing SHA256SUMS.txttar -xzf nxdns-0.0.25-x86_64-linux-musl.tar.gzcd nxdns-0.0.25-x86_64-linux-muslThe signature covers SHA256SUMS.txt, and SHA256SUMS.txt covers the tarball. Verify a release explains where the public key comes from and which fingerprint to expect; read it before your first install.
The directory holds the binary nxdns, the systemd unit nxdns.service, the sysusers file nxdns.conf that creates the nxdns user, the licences and a short INSTALL.md.
2. Install the binary, the user and the unit
As root:
install -m 0755 nxdns /usr/local/bin/nxdnsinstall -m 0644 nxdns.conf /usr/lib/sysusers.d/nxdns.confsystemd-sysusersinstall -m 0644 nxdns.service /etc/systemd/system/nxdns.servicesystemctl daemon-reloadmkdir -p -m 0755 /etc/nxdnsDo not create /var/lib/nxdns or /var/log/nxdns. systemd creates them on the first start, with the right owner and modes.
3. Write the configuration
The smallest configuration that starts is a group named default and one upstream resolver. Write /etc/nxdns/config.zon and give the web interface a password:
.{ .groups = .{ .{ .name = "default" } }, .upstreams = .{ .{ .url = "https://cloudflare-dns.com/dns-query" } }, .web = .{ .password = "choose-a-real-password" },}The file holds a password in plain text, so restrict it:
chown root:nxdns /etc/nxdns/config.zonchmod 0640 /etc/nxdns/config.zonCheck it, then load it into the database:
nxdns check --config /etc/nxdns/config.zonnxdns import /etc/nxdns/config.zonchecking configuration file /etc/nxdns/config.zonOK upstreams[0] https://cloudflare-dns.comOK: no problems foundinfo(migrations): config.db migrated from schema version 0 to 1imported /etc/nxdns/config.zoncheck sends one real query through each upstream, so the machine needs working DNS when you run it. If it finds a problem it lists every one and exits 2.
The password is stored only as a hash, so once you have logged in (step 5) you can delete the file:
rm /etc/nxdns/config.zonFrom now on you change settings in the web interface. This is database mode, and it is what the packaged unit runs. To keep config.zon in git as the configuration instead, keep the file and see Run in file mode.
4. Start it
systemctl enable --now nxdnsjournalctl -u nxdns -fA healthy start names every socket it opened:
info(nxdns): nxdns 0.0.25 serving on udp [::]:53 tcp [::]:53 tcp 0.0.0.0:53; 1 upstream(s); blocklist generation 1The unit grants the capability to bind port 53 and runs nxdns as the nxdns user. It does not restart nxdns after exit code 2 (a configuration problem) or 64 (a wrong command line), because a retry cannot fix either; the journal says what is wrong. Fix it and start the service again.
If port 53 is already taken
cannot bind udp [::1]:53: AddressInUsenxdns run failed: AddressInUseOn most systemd distributions systemd-resolved holds port 53 with its stub listener. Turn the stub off and keep resolved for the machine’s own lookups:
mkdir -p /etc/systemd/resolved.conf.dprintf '[Resolve]\nDNSStubListener=no\n' > /etc/systemd/resolved.conf.d/nxdns.confsystemctl restart systemd-resolvedIf /etc/resolv.conf is a link to /run/systemd/resolve/stub-resolv.conf, point it at /run/systemd/resolve/resolv.conf so the machine still resolves names. ss -lnup 'sport = :53' shows what else holds the port.
5. Check it and point your network at it
From another machine on your network:
dig @<server-ip> example.com A +shortOpen http://<server-ip>:8080 and log in with the password from step 3. Then add blocklists on the Protection page, as Get started shows.
To make every device use nxdns, set the DNS server your router hands out (its DHCP settings) to the nxdns machine’s address. Leave the nxdns machine itself on another resolver: nxdns looks up its upstreams’ names through it, and that should not depend on nxdns already running.
Run in file mode
In file mode /etc/nxdns/config.zon is the configuration. Every start applies the file, and the web interface refuses configuration changes and names the file instead. Use it when the file lives in git and a tool deploys it. The configuration model compares the two modes.
Switch to file mode
Stop the service first, so no change made in the web interface is lost, then write the current configuration out as the file:
systemctl stop nxdnsnxdns export --out /etc/nxdns/config.zonchown root:nxdns /etc/nxdns/config.zonchmod 0640 /etc/nxdns/config.zonnxdns check --config /etc/nxdns/config.zonAdd a drop-in that passes the file, and start:
mkdir -p /etc/systemd/system/nxdns.service.dcat > /etc/systemd/system/nxdns.service.d/file-mode.conf <<'EOF'[Service]ExecStart=ExecStart=/usr/local/bin/nxdns run --config=/etc/nxdns/config.zonEOFsystemctl daemon-reloadsystemctl start nxdnsThe empty ExecStart= line is required: it clears the unit’s own command instead of adding a second one. The first start changes nothing, because the file was written from the database it now governs:
reconciled '/etc/nxdns/config.zon': no changesinfo(nxdns): authority: file (/etc/nxdns/config.zon)Blocklists, their downloads and the query history all carry over.
Change the configuration
Edit the file, check it, restart:
$EDITOR /etc/nxdns/config.zonnxdns check --config /etc/nxdns/config.zonsystemctl restart nxdnsThe restart logs what it changed:
reconciled '/etc/nxdns/config.zon': upstreams +1 ~0 -0; settings +0 ~2 -0;settings keys changed: dns.port web.portBecause the file is read at every start, a broken file stops the service at its next restart, whenever that is. If a tool deploys the file, run nxdns check --config before it restarts nxdns, so a mistake fails the deploy instead.
Go back to database mode
Remove the drop-in and restart. The database already holds the last configuration the file set:
rm /etc/systemd/system/nxdns.service.d/file-mode.confsystemctl daemon-reloadsystemctl restart nxdnsinfo(nxdns): authority: database