{ pkgs, lib, ... }: # Pi-hole via the official container (the native nixpkgs pihole-ftl module # segfaults on aarch64 / Pi 3B+). Host networking so it can serve DHCP and reach # the host's unbound at 127.0.0.1:5335. Config via FTLCONF_* env vars (pihole v6) # — these override pihole.toml on every start, so it stays effectively # declarative. The web admin password is added from sops in the host config. { virtualisation.podman = { enable = true; dockerCompat = true; }; virtualisation.oci-containers = { backend = "podman"; containers.pihole = { image = "pihole/pihole:latest"; # TODO: pin to the tested version after VM check autoStart = true; extraOptions = [ "--network=host" # DHCP broadcast + host unbound on 127.0.0.1 "--cap-add=NET_ADMIN" # DHCP "--cap-add=NET_RAW" # DNS engine (dnsmasq) — REQUIRED "--cap-add=SYS_NICE" "--cap-add=CHOWN" # entrypoint chowns /etc/pihole ]; volumes = [ "/var/lib/pihole:/etc/pihole" ]; # persist config/state environment = { TZ = "Europe/Berlin"; FTLCONF_dns_upstreams = "127.0.0.1#5335"; # host unbound (recursive) FTLCONF_dns_listeningMode = "all"; # serve the LAN FTLCONF_dns_domain = "sol"; FTLCONF_dhcp_active = "true"; FTLCONF_dhcp_start = "10.0.0.50"; FTLCONF_dhcp_end = "10.0.0.200"; FTLCONF_dhcp_router = "10.0.0.1"; FTLCONF_dhcp_leaseTime = "1h"; # Arrays (format validated in the VM): static lease + local DNS records. FTLCONF_dhcp_hosts = "00:e0:4c:3c:a3:1f,10.0.0.20,jupiter"; FTLCONF_dns_hosts = "10.0.0.10 mercury.sol;10.0.0.20 jupiter.sol"; }; }; }; # Bind-mount source must exist (podman won't create it). systemd.tmpfiles.rules = [ "d /var/lib/pihole 0755 root root -" ]; networking.firewall.allowedTCPPorts = [ 53 80 ]; networking.firewall.allowedUDPPorts = [ 53 67 ]; }