Skip to content

Pi-hole

Network DNS, kept independent of the Swarm cluster deliberately (a Swarm node restart or redeploy must never take down the network's DNS resolver).

Runtime VM (Proxmox), cloud-init template
Node pve
IP 10.36.0.11
MAC (net0) bc:24:11:f2:0d:90 (see Network)
Image pihole/pihole:2026.07.2 (run via Docker Compose on the VM, not Swarm)
URL https://pihole.lab.riversfamily.uk (web UI)
Auth Own login
Source ansible/roles/pihole/

DNS itself (port 53) is served directly on the LAN, not through Traefik — one of only two non-HTTP exceptions to the single-ingress rule (the other is UniFi's device ports).

Configuration

Variable Default Set in
pihole_web_port 8080 ansible/roles/pihole/defaults/main.yml
pihole_upstream_dns [1.1.1.1, 1.0.0.1] same
pihole_web_password {{ vault_pihole_web_password }} same
pihole_swarm_core_ip "" (empty) same — set to enable the split-horizon wildcard

Split-horizon DNS: when pihole_swarm_core_ip is set, the role templates a dnsmasq config so *.lab.riversfamily.uk resolves internally to the swarm-core IP (where Traefik listens) — this is what lets internal clients reach every service by name without a public DNS round-trip.

Provisioning also disables systemd-resolved's stub listener (a drop-in config, applied with an immediate flush_handlers rather than deferred to end-of-play) and repoints /etc/resolv.conf at /run/systemd/resolve/resolv.conf, since Pi-hole needs port 53 for itself.

Gotchas

  • Single point of failure for DNS. No secondary resolver is planned; accepted, in the same spirit as the single-Swarm-manager quorum limit.
  • pihole_timezone defaults to {{ timezone | default('Etc/UTC') }}, but no plain timezone variable is set anywhere in this repository — only common_timezone (Europe/London) is defined, and only the common role consumes it. Pi-hole's container therefore runs on Etc/UTC, not Europe/London, unless this is fixed. Worth confirming before relying on Pi-hole's logs/query timestamps being in local time.