# Install nxdns

This directory is an nxdns release for one architecture. It holds:

| File | What it is |
| --- | --- |
| `nxdns` | The static binary, mode 0755 |
| `nxdns.service` | The systemd unit |
| `nxdns.conf` | The sysusers fragment that creates the `nxdns` user |
| `LICENSE` | EUPL-1.2 |
| `THIRD-PARTY-NOTICES` | Licences of everything compiled or bundled in |
| `INSTALL.md` | This file |

The binary is statically linked against musl and needs nothing installed on the target host.

Verify the download before you trust it. `docs/how-to/verify-a-release.md` in the repository covers where the public key comes from, what fingerprint to expect, and what the signature does and does not prove.

## 1. Install the binary, the user and the unit

Run as root on the target host:

```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
```

`nxdns.conf` ships under the name it is installed as, so there is no rename to get wrong.

Do not create `/var/lib/nxdns` or `/var/log/nxdns` by hand. The unit's `StateDirectory` and `LogsDirectory` settings make systemd create them on first start, `/var/lib/nxdns` at mode 0700 owned by `nxdns`.

## 2. Write the configuration

nxdns will not start with nothing to forward to. Write `/etc/nxdns/config.zon`:

```zon
.{
    .groups = .{ .{ .name = "default" } },
    .upstreams = .{ .{ .url = "https://cloudflare-dns.com/dns-query" } },
    .web = .{ .password = "choose-a-real-password" },
}
```

That file holds a password in plain text. Root's umask is 022 on most distributions, so restrict it as soon as you have written it:

```sh
chown root:nxdns /etc/nxdns/config.zon
chmod 0640 /etc/nxdns/config.zon
```

0640 with group `nxdns` rather than 0600: the service runs as `nxdns`, and systemd leaves `/etc/nxdns` owned by root. Keep that group read bit for good. Under `run --config` the service reads this file on **every** start, not once, so tightening the mode after the first boot breaks the next restart. Under database authority it is `nxdns import` that reads the file, as whoever runs that command, and a bare `nxdns run` never reads it at all.

Check it before starting the service:

```sh
nxdns check --config /etc/nxdns/config.zon
```

A good file ends with `OK: no problems found`. Exit 2 means `check` found something to fix and printed every problem it found. The upstream probe sends a real query, so this needs working DNS on the host.

Load it into the database:

```sh
nxdns import /etc/nxdns/config.zon
```

The packaged unit runs `nxdns run` with no `--config`, so from here the database is the configuration and nothing reads the file again. `web.password` is hashed and the plaintext is never stored, so once you have logged in you can delete the file:

```sh
rm /etc/nxdns/config.zon
```

A kept file is not a backup. `nxdns export` is.

To keep the file as the configuration instead — converged at every start, with the UI refusing configuration edits — do not delete it, and add a drop-in that appends `--config=/etc/nxdns/config.zon` to `ExecStart`. See `docs/how-to/install-with-systemd.md`.

## 3. Start it

```sh
systemctl enable --now nxdns
journalctl -u nxdns -f
```

A healthy start logs a line naming every socket it bound. Port 53 is privileged, and the unit grants `CAP_NET_BIND_SERVICE` through `AmbientCapabilities`.

## 4. Confirm it answers

From another machine on the LAN:

```sh
dig @<server-ip> example.com A +short
```

The admin interface is on port 8080 by default; log in with the password from the configuration file. `http://<server-ip>:8080/api/health` reports upstream availability and disk state without a login.

## More

The full documentation lives in the repository at
<https://git.mial.net/mokhtar/nxdns>:

- `docs/how-to/install-with-systemd.md` — this procedure with the reasoning
- `docs/how-to/verify-a-release.md` — verifying the download
- `docs/how-to/troubleshoot.md` — first-install failures
- `docs/reference/configuration.md` — every configuration field
- `docs/reference/cli.md` — every command and flag
