4.0 KiB
NixOS module: services.nxdns
Owner ruling (2026-10-20): ship nixosModules.default from the flake, with the options enable, package, configFile and openFirewall, file mode only, no settings attrset, and the shipped nxdns.service reused through systemd.packages plus a drop-in. The owner left Nix in CI, the VM test and a distinct exit code for SchemaTooNew to the implementer, within one constraint: the self-hosted Gitea runner (../mial.net/manifests/gitea/) is shared and must not be monopolized.
Why
The flake already shipped a package and had one consumer, rpi.mial.net, which wrote its own module. Four parts of that module are not specific to the Pi, and two encode incidents: the build-time nxdns import validation, and restartTriggers, without which a configuration-only deploy restarted nothing (2026-08-16). A package with no service is half an install on NixOS.
What ships
nix/module.nix.configFilegoes throughnxdns import --data-dir $TMPDIR/validatefrompackagein arunCommand, and the result is/etc/nxdns/config.zon. Thenxdnssystem user and group.systemd.packages = [ package ], and a drop-in that clearsExecStartand setsrun --config=/etc/nxdns/config.zon, clearsConfigurationDirectorybecauseenvironment.etcowns/etc/nxdns, addswantedByandrestartTriggers.openFirewallopens 53/udp and 53/tcp and nothing else.- The package installs the tarball's
nxdns.servicetolib/systemd/system/withExecStartpointed at its own binary.--replace-failmakes a changedExecStartline in the unit a package build failure, not a silent mismatch. - Not shipped: database mode, a Nix-to-ZON renderer, and the Pi's choices about its own resolver, which stay in
rpi.mial.net.
Decisions left to the implementer
- No Nix in CI. No job installs Nix today. The checks need a nixpkgs closure and, for the VM test, KVM; downloading that on every push costs the shared runner disk and network, and the runner's cache already caused a disk-pressure eviction on 2026-08-23. The cut's existing
nix flake check --no-buildevaluates both checks at every release, which catches a module that no longer evaluates. - The VM test runs on the developer's host.
checks.x86_64-linux.nixos-moduleandchecks.x86_64-linux.nixos-module-rejects-configare built locally, with KVM, after any change to the module, the checks, the package or the unit. AGENTS.md says so. Whether the runner node exposes/dev/kvmwas not checked, and nothing here depends on it. - No new exit code.
SchemaTooNewis already inconfig/faults.zig, sorunexits 2 for it and the unit'sRestartPreventExitStatus=2 64stops instead of restart-looping (src/app.zigtests the mapping). A NixOS rollback onto a migrated database therefore stops the unit, anddocs/how-to/upgrade.mddescribes the recovery. The exit-1 behaviour in the Pi module's notes dates from 0.0.2.
Acceptance
nix flake check --no-buildpasses.nixos-module: a VM with the module andopenFirewallanswersnas.lanfrom a local record;systemctl catshows the packaged unit and the drop-in; the service runs asnxdns; the effectiveConfigurationDirectoryis empty,ReadOnlyPathsstill holds/etc/nxdns, and/etc/nxdnsbelongs to root; port 53 is open over UDP and TCP;switch-to-configuration testinto a specialisation that changes onlyconfigFilegives a newInvocationIDand the new answer. WithoutrestartTriggersthe test fails on the restart assertion, and without theConfigurationDirectoryclear it fails on that one (each checked once by removing the line).nixos-module-rejects-config: a system build with an invalid regex rule fails, and its log carriesFAIL rules[0].pattern.docs/how-to/install-with-nix.mddocuments the module, the options, the store-readability ofconfigFile, and rollback.
After the release
rpi.mial.net replaces its module with services.nxdns and keeps its Pi-only parts, through the rpi-nixos-iac peer.