Files
nxdns/docs/how-to/install-with-systemd.md
T

7.1 KiB

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-release
curl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v<version>/nxdns-<version>-x86_64-linux-musl.tar.gz
curl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v<version>/SHA256SUMS.txt
curl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v<version>/SHA256SUMS.txt.asc
gpg --verify SHA256SUMS.txt.asc SHA256SUMS.txt
sha256sum -c --ignore-missing SHA256SUMS.txt
tar -xzf nxdns-<version>-x86_64-linux-musl.tar.gz
cd nxdns-<version>-x86_64-linux-musl

The 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/nxdns
install -m 0644 nxdns.conf /usr/lib/sysusers.d/nxdns.conf
systemd-sysusers
install -m 0644 nxdns.service /etc/systemd/system/nxdns.service
systemctl daemon-reload
mkdir -p -m 0755 /etc/nxdns

Do 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.zon
chmod 0640 /etc/nxdns/config.zon

Check it, then load it into the database:

nxdns check --config /etc/nxdns/config.zon
nxdns import /etc/nxdns/config.zon
checking configuration file /etc/nxdns/config.zon
OK upstreams[0] https://cloudflare-dns.com
OK: no problems found
info(migrations): config.db migrated from schema version 0 to 1
imported /etc/nxdns/config.zon

check 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.zon

From 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 nxdns
journalctl -u nxdns -f

A healthy start names every socket it opened:

info(nxdns): nxdns <version> serving on udp [::]:53 tcp [::]:53 tcp 0.0.0.0:53; 1 upstream(s); blocklist generation 1

The 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: AddressInUse
nxdns run failed: AddressInUse

On 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.d
printf '[Resolve]\nDNSStubListener=no\n' > /etc/systemd/resolved.conf.d/nxdns.conf
systemctl restart systemd-resolved

If /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 +short

Open 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 nxdns
nxdns export --out /etc/nxdns/config.zon
chown root:nxdns /etc/nxdns/config.zon
chmod 0640 /etc/nxdns/config.zon
nxdns check --config /etc/nxdns/config.zon

Add a drop-in that passes the file, and start:

mkdir -p /etc/systemd/system/nxdns.service.d
cat > /etc/systemd/system/nxdns.service.d/file-mode.conf <<'EOF'
[Service]
ExecStart=
ExecStart=/usr/local/bin/nxdns run --config=/etc/nxdns/config.zon
EOF
systemctl daemon-reload
systemctl start nxdns

The 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 changes
info(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.zon
nxdns check --config /etc/nxdns/config.zon
systemctl restart nxdns

The restart logs what it changed:

reconciled '/etc/nxdns/config.zon': upstreams +1 ~0 -0; settings +0 ~2 -0;
settings keys changed: dns.port web.port

Because 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.conf
systemctl daemon-reload
systemctl restart nxdns
info(nxdns): authority: database