# 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: ```sh mkdir -p ~/nxdns-release && cd ~/nxdns-release curl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v/nxdns--x86_64-linux-musl.tar.gz curl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v/SHA256SUMS.txt curl -fLO https://git.mial.net/mokhtar/nxdns/releases/download/v/SHA256SUMS.txt.asc gpg --verify SHA256SUMS.txt.asc SHA256SUMS.txt sha256sum -c --ignore-missing SHA256SUMS.txt tar -xzf nxdns--x86_64-linux-musl.tar.gz cd nxdns--x86_64-linux-musl ``` The signature covers `SHA256SUMS.txt`, and `SHA256SUMS.txt` covers the tarball. [Verify a release](verify-a-release.md) 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: ```sh 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: ```zon .{ .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: ```sh chown root:nxdns /etc/nxdns/config.zon chmod 0640 /etc/nxdns/config.zon ``` Check it, then load it into the database: ```sh 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: ```sh 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](#run-in-file-mode). ## 4. Start it ```sh systemctl enable --now nxdns journalctl -u nxdns -f ``` A healthy start names every socket it opened: ``` info(nxdns): nxdns 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: ```sh 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: ```sh dig @ example.com A +short ``` Open `http://:8080` and log in with the password from step 3. Then add blocklists on the **Protection** page, as [Get started](../tutorial/get-started.md#5-add-a-blocklist) 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](../explanation/configuration-model.md) 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: ```sh 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: ```sh 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: ```sh $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: ```sh rm /etc/systemd/system/nxdns.service.d/file-mode.conf systemctl daemon-reload systemctl restart nxdns ``` ``` info(nxdns): authority: database ```