Pi-hole
Pi-hole is software that blocks ads and trackers across your entire network.
It provides a mature, trusted, integrated service that includes a web interface, DHCP server, and DNS server.
Presumptions
- NixOS 25.11 xantusia (or newer)
- Classic channels configuration
- Personal home network
- IPv4
- Homelab skills
- Patient users while you tinker and stabilize
Please extrapolate for other NixOS versions, configurations, flakes, et cetera.
Basic Setup
Create /etc/nixos/pi-hole.nix and add the following. Replace homelab.me and the IPs with your own DNS domain and network addresses. If you don’t have a DNS domain, you can create your own. No fee or registration is required. It’s private to your network.
# # pi-hole.nix # # Notes # - Pi-hole doesn't have a mechanism to manage Groups, Clients, or # Domains. Use the web gui. # - https://docs.pi-hole.net/group_management/example/ # {lib, ...}: { # # Networking # # Essential infrastructure # - List your most essential network resources here networking = { hosts = { "192.168.33.1" = ["gateway.homelab.me" "gateway"]; "192.168.33.2" = ["pi-hole.homelab.me" "pi-hole"]; "192.168.33.15" = ["nas.homelab.me" "nas"]; }; }; # # Services # services = { # I'm not actually using the dnsmasq service. Pi-hole provides # it's own dnsmasq. I'm using Nix' ability to manage the # dnsmasq-style configuration file that Pi-hole utilizes. dnsmasq = { enable = false; settings = { address = [ "/feelinsonice-hrd.appspot.com/ # Block Snapchat" "/feelinsonice.appspot.com/ # Block Snapchat" "/snapchat.com/ # Block Snapchat" ]; dhcp-name-match = [ "set:hostname-ignore,wpad" "set:hostname-ignore,localhost" ]; # Set DHCP option 6 to the DNS server you nodes should use. dhcp-option = [ "vendor:MSFT,2,1i" "6,192.168.33.2" ]; domain = [ "homelab.me,192.168.33.0/24,local" ]; }; }; pihole-ftl = { enable = true; lists = [ { url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"; type = "block"; enabled = true; description = "Steven Black's HOSTS"; } ]; openFirewallDNS = true; openFirewallDHCP = true; openFirewallWebserver = true; queryLogDeleter.enable = true; settings = { dhcp = { active = false; # <-- SET TO TRUE ONLY WHEN YOU'RE READY! end = "192.168.33.254"; hosts = [ "00:00:5e:00:53:01,192.168.33.22,jane-laptop" "00:00:5e:00:53:ab,bill-desktop" "00:00:5e:00:53:ff,office-printer" ]; ipv6 = false; leaseTime = "24h"; start = "192.168.33.61"; rapidCommit = true; resolver = { resolveIPv6 = false; }; router = "192.168.33.1"; }; # misc.readOnly = false; dns = { cnameRecords = [ "color-printer,office-printer" "color-printer.homelab.me,office-printer.homelab.me" ]; domain = "homelab.me"; domainNeeded = true; expandHosts = true; interface = "eth0"; hosts = [ "192.168.33.1 gateway" "192.168.33.2 pi-hole" "192.168.33.15 nas" ]; upstreams = ["1.1.1.1" "1.1.1.2"]; }; # Let's not use Pi-hole time service. My home router provides clock. ntp = { ipv4.active = false; ipv6.active = false; sync.active = false; }; webserver = { api = { # To manage the web login: # 1) Temporarily set misc.readOnly to false in # configuration.nix and switch to it. # 2) Manually set a password: # Pi-hole web console > Settings > All settings > # Webserver and API > webserver.api.password > Value: ****** # 3) Read the generated hash: # sudo pihole-FTL --config webserver.api.pwhash pwhash = "$BALLOON-SHA256..."; }; session = { timeout = 43200; # 12h }; }; }; useDnsmasqConfig = true; }; pihole-web = { enable = true; ports = [80]; }; resolved = { extraConfig = '' DNSStubListener=no MulticastDNS=off ''; }; }; # # System # system.activationScripts = { print-pi-hole = { text = builtins.trace "building the pi-hole configuration..." ""; }; }; # # Systemd # # The following silences a benign FTL.log warning: # WARNING API: Failed to read /etc/pihole/versions (key: internal_error) systemd.tmpfiles.rules = [ # Type Path Mode User Group Age Argument "f /etc/pihole/versions 0644 pihole pihole - -" ]; }
Update your configuration.nix to import ./pi-hole.nix
imports = [ ./hardware-configuration.nix ./pi-hole.nix ]
nixos-switch rebuild --upgradeNote
- This command presumes that you are using classic NixOS channels and not an experimental flakes configuration.
- This command will freshen all installed packages and configuration settings.
Browse to your new Pi-Hole service to review it and learn more.
Example: http://192.168.33.2
Avoid making settings in the GUI. Most are managed via configuration.nix, except for Group Management: Groups, Clients, and Domains. You can also temporarily disable ad/tracking blocking in the web GUI.
Commentary
I’ve been running Pi-hole for at least three years. It’s a gem of a service that markedly improves your web browsing experience.
It was my last non-NixOS homelab service. I’m pleased that 25.11 xantusia now supports it. Please consider donating to NixOS and Pi-hole. They provide value to the public and would appreciate you reciprocating.
I’ve had almost no problems or negative feedback, with one notable exception. When family was applying for jobs online, I had to temporarily disable ad/tracker blocking via Pi-hole > DNS Control > Disable Blocking. I was irked to learn that mainstream companies utilize ads/trackers when submitting a resume. I’m not talking about sites like LinkedIn, which works fine. I’m talking about the employers themselves.
I host my NixOS Pi-hole in a lightweight LXC. I use two of them and a service called Keepalived to reduce the risk of an outage.