{ pkgs, config, hostname, ... }: let ctrld = pkgs.stdenv.mkDerivation rec { pname = "ctrld"; version = "1.5.0"; src = pkgs.fetchurl { url = "https://github.com/Control-D-Inc/ctrld/releases/download/v${version}/ctrld_${version}_linux_amd64.tar.gz"; # Run: nix-prefetch-url --unpack https://github.com/Control-D-Inc/ctrld/releases/download/v1.5.0/ctrld_1.5.0_linux_amd64.tar.gz sha256 = "sha256-rMFSeKxJ/3ENsVlgKiBIC411/nv4PEQjufTthFx0USc="; }; sourceRoot = "."; installPhase = '' install -Dm755 dist/ctrld_${version}_linux_amd64/ctrld $out/bin/ctrld ''; }; in { environment.systemPackages = [ ctrld ]; # ctrld listens on 127.0.0.1:53, tell systemd-resolved to use it services.resolved = { enable = true; settings.Resolve = { DNS = "127.0.0.1"; DNSOverTLS = "no"; FallbackDNS = ""; }; }; age.secrets.ctrld-config.file = ./secrets/ctrld-${hostname}.age; # Ensure ctrld starts before resolved. Must NOT be `after network.target`: # systemd-resolved.service is Before=network.target, so that would create # an ordering cycle (network.target -> resolved -> ctrld -> network.target) # that systemd breaks by dropping jobs, taking dbus-broker/logind down with it. # # DefaultDependencies must also be off: every unit gets an implicit # After=basic.target unless it opts out, and basic.target -> sysinit.target # -> (Before=) systemd-resolved.service -> (Before=) ctrld.service -> # basic.target recreates the exact same cycle even with network.target gone. # Observed again on 2026-08-22: systemd deleted dbus-broker.service's start # job to break it, which silently took NetworkManager down with it (it never # started until manually run) since NM is D-Bus activated. systemd.services.ctrld = { description = "ControlD DNS Proxy"; before = [ "systemd-resolved.service" "shutdown.target" ]; after = [ "agenix.service" ]; conflicts = [ "shutdown.target" ]; wants = [ "agenix.service" ]; wantedBy = [ "multi-user.target" ]; unitConfig.DefaultDependencies = false; restartTriggers = [ config.age.secrets.ctrld-config.file ]; serviceConfig = { RuntimeDirectory = "ctrld"; ExecStartPre = "${pkgs.coreutils}/bin/install -m 600 ${config.age.secrets.ctrld-config.path} /run/ctrld/config.toml"; ExecStart = "${ctrld}/bin/ctrld run --config /run/ctrld/config.toml"; Restart = "on-failure"; }; }; }