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

111 lines
5.1 KiB
Markdown

# Install with Nix
Add nxdns to a NixOS machine as a flake input pinned to a release, and run it with the flake's `services.nxdns` module. At the end the machine answers DNS on port 53 from a configuration file that was checked when the system was built.
The flake does not build nxdns from source. Its package fetches the release tarball for `x86_64-linux` or `aarch64-linux` and pins its SHA-256, so a changed byte fails the build.
## 1. Add the input
In your flake:
```nix
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nxdns = {
url = "git+https://git.mial.net/mokhtar/nxdns.git?ref=refs/tags/v<version>";
inputs.nixpkgs.follows = "nixpkgs";
};
};
```
Pin a tag, not a branch: a tag never moves under you. The `follows` line saves fetching and evaluating a second nixpkgs. Then write the lock entry:
```sh
nix flake lock
```
## 2. Enable the service
Write the configuration next to your NixOS configuration, for example as `nxdns-config.zon`. The smallest one that starts is a group named `default` and one upstream resolver:
```zon
.{
.groups = .{ .{ .name = "default" } },
.upstreams = .{ .{ .url = "https://cloudflare-dns.com/dns-query" } },
}
```
Import the module and enable it:
```nix
{ inputs, ... }:
{
imports = [ inputs.nxdns.nixosModules.default ];
services.nxdns = {
enable = true;
configFile = ./nxdns-config.zon;
openFirewall = true;
};
}
```
Then run `nixos-rebuild switch`.
| Option | Default | What it does |
| --- | --- | --- |
| `enable` | `false` | Runs nxdns as `nxdns.service`, as the `nxdns` system user. |
| `package` | the release of the pinned tag | The nxdns release to run. |
| `configFile` | none, required | The configuration, installed as `/etc/nxdns/config.zon`. |
| `openFirewall` | `false` | Opens port 53 over UDP and TCP. The web interface (8080) and the DoH and DoT ports stay closed; open them yourself if other machines should reach them. |
`configFile` is copied into the Nix store, which every local user can read. Do not put a plaintext `web.password` in it. Use the `web.password_hash` from an export instead, as [Set up admin authentication](set-up-admin-authentication.md#put-a-hash-in-the-file-instead) shows.
What the module does:
- **The build checks the configuration.** The pinned nxdns loads `configFile` into a throwaway database during the build, so a file nxdns would reject fails `nixos-rebuild` with nxdns's own message, for example `FAIL rules[0].pattern: '(' is not a valid regex pattern`, and nothing is deployed. The build has no network, so it cannot check that the upstreams answer or load DoH and DoT certificates; a missing certificate still stops the service when it starts.
- **nxdns runs in file mode.** Every start applies the file, and the web interface shows the configuration read-only. To change anything, change the file and switch. A switch that changes only the configuration restarts the service.
- **The unit is the packaged one**, with its sandboxing. The module only replaces its command line; `systemctl cat nxdns.service` shows both.
The module does not touch the machine's own resolver. Point your network at nxdns by setting the DNS server your router hands out to this machine's address, and leave `networking.nameservers` on another resolver, so the machine can still resolve names while nxdns is down.
The package alone is `inputs.nxdns.packages.${pkgs.stdenv.hostPlatform.system}.default`, for running nxdns some other way. It installs `bin/nxdns` and `lib/systemd/system/nxdns.service`, and sets `meta.mainProgram`.
## 3. Upgrade
Read the release notes, change the tag in the input URL, then:
```sh
nix flake lock --update-input nxdns
nixos-rebuild switch
```
Rolling back to an older generation puts an older nxdns in front of a database the newer one may already have upgraded. The older binary then refuses to start; [Upgrade](upgrade.md#going-back-to-an-older-release) explains the way back.
## Let Renovate bump the tag
Renovate's built-in `nix` manager cannot do this: it only moves `flake.lock` along the ref an input already follows, and a tag ref never moves. A regex custom manager that reads the tag from the input URL, with the `gitea-tags` datasource, does:
```json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"customManagers": [
{
"customType": "regex",
"managerFilePatterns": ["/^flake\\.nix$/"],
"matchStrings": ["git\\+https://git\\.mial\\.net/mokhtar/nxdns\\.git\\?ref=refs/tags/(?<currentValue>v\\d+\\.\\d+\\.\\d+)"],
"depNameTemplate": "mokhtar/nxdns",
"datasourceTemplate": "gitea-tags",
"registryUrlTemplate": "https://git.mial.net",
"versioningTemplate": "semver"
}
]
}
```
Its pull request changes the tag in `flake.nix` only. The same pull request also needs a refreshed `flake.lock`: run `nix flake update nxdns` as a `postUpgradeTasks` command (self-hosted Renovate, with `allowedCommands`), or have CI commit the lock on the Renovate branch.
A tag can exist for a short while before its release files are uploaded. A pin made by hand in that window fails the build with a 404 from the download URL; build again once the release is published.