NixOS Pi-hole Unbound
I recently decommissioned the last Ubuntu server in my rack and replaced it with NixOS. My entire rack is Nix at this point 😅 I never saw that coming, even a year ago haha.
- NixOS x86 router
- NixOS x86 DNS server
- NixOS x86 primary compute & storage
When I set out to get Pi-hole running on NixOS I figured cobbling together a working module in an afternoon was in the cards 😅 yup.. I was wrong and it's a little harder. So after blowing a Sunday afternoon on trying to flakeify a Pi-hole and Unbound setup I defaulted to Podman and Nix's virtualisation.oci-containers.containers
which you can think of like a declarative compose
. But the difference is your containers run as systemd services
, and you still manage the podman containers with your normal tooling like podman exec -it <container> bash
. You can debug your containers with systemctl
and jourlctl
commands, and I guess you can docker logs -f <container>
too.
Containers
{
virtualisation = {
containers.enable = true;
podman.enable = true;
oci-containers.containers = {
"pihole" = {
autoStart = true;
image = "pihole/pihole:latest";
environment = {
TZ = "America/Edmonton";
# Set password after
# docker exec -ti <name_of_your_container> pihole -a -p
# set a secure password here or it will be random
# WEBPASSWORD= "set password"
};
ports = [
"53:53/udp"
"53:53/tcp"
"80:80/tcp"
];
volumes = [
"/pihole/data:/etc/pihole"
"/pihole/dnsmasq:/etc/dnsmasq.d"
];
};
"unbound" = {
autoStart = true;
image = "mvance/unbound:latest";
ports = [
"5053:53/tcp"
"5053:53/udp"
];
volumes = [
"/pihole/unbound/data:/opt/unbound/etc/unbound/"
"/pihole/unbound/rules/unbound.conf:/opt/unbound/etc/unbound/unbound.conf"
"/pihole/unbound/rules/a-records.conf:/opt/unbound/etc/unbound/a-records.conf"
"/pihole/unbound/rules/srv-records.conf:/opt/unbound/etc/unbound/srv-records.conf"
"/pihole/unbound/rules/forward-records.conf:/opt/unbound/etc/unbound/forward-records.conf"
"/pihole/unbound/rules/root.hints:/var/lib/unbound/root.hints"
];
};
};
};
}
server:
# If no logfile is specified, syslog is used
# logfile: "/var/log/unbound/unbound.log"
verbosity: 0
interface: 127.0.0.1
port: 5053
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
prefer-ip6: no
#root-hints: "/var/lib/unbound/root.hints"
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: no
edns-buffer-size: 1232
prefetch: yes
num-threads: 1
# Ensure kernel buffer is large enough to not lose messages in traffic spikes
so-rcvbuf: 1m
# Ensure privacy of local IP ranges
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
# tailnet reserved
private-address: 100.0.0.0/8
Unbound
Remember to update this file every 6 months.
wget -O root.hints https://www.internic.net/domain/named.root
Tailscale
If you are running Tailscale on your Linux desktop your device will fall back to your tailnets
MagicDNS server which has its own public resolver @ 100.100.100.100:53
, which will bullocks your blocking endeavors. Android seems to respect your local networks DNS, but if you disconnect from WiFi ✨ TaDaaaa you are free to bypass blocking rules.
Is your DNS server a tailscale device already? If so, go to tailscale dashboard -> DNS -> Add global nameservers
make sure its set to override. Put your tailscale IP address for your DNS server 🤯 works like a dream & you can leave MagicDNS on and still get all the tailscale DNS goodness 🥰.