Files
truenas-stacks/pihole

PD Pi-hole / Unbound / Keepalived Stack

Primary-node stack for the Pi-hole HA plan.

  • Repo stack path: /home/fizzlepoof/repos/truenas-stacks/pihole
  • Live PD stack path: /mnt/docker-ssd/docker/compose/pihole
  • Node role: PD primary / MASTER
  • VIP target: 10.5.1.53/24

This stack intentionally covers the PD primary plus PD-hosted sync:

  • Pi-hole
  • Unbound
  • Keepalived
  • Nebula Sync

NOMAD and RPi4 replicas still need their own deployment steps.

Files

  • docker-compose.yaml — primary-node stack
  • .env.example — required variables
  • unbound.conf — shared recursive resolver config
  • keepalived.conf.template — rendered into keepalived.conf
  • bin/render_keepalived_conf.py — generates keepalived.conf from .env
  • bin/prepare_pd.sh — creates appdata dirs, downloads root hints, renders config, validates compose, optional --up

First-time setup on PD

cd /mnt/docker-ssd/docker/compose/pihole
cp .env.example .env
nano .env   # set real passwords + verify LAN_INTERFACE
/usr/bin/bash bin/prepare_pd.sh

If that validates cleanly, bring it up:

/usr/bin/bash bin/prepare_pd.sh --up

Important notes

  • KEEPALIVED_PASSWORD must be 8 chars or fewer because VRRP PASS auth is annoying and old.
  • Verify LAN_INTERFACE on PD before deployment (ip link show).
  • FTLCONF_dns_listeningMode=LOCAL is set because TrueNAS binds loopback on port 53.
  • Pi-hole NTP is explicitly disabled here because PD host chronyd already owns UDP 123.
  • Nebula Sync runs on PD and uses NEBULA_SYNC_PRIMARY / NEBULA_SYNC_REPLICAS from .env.
  • Nebula Sync is intentionally set to selective mode with gravity sync toggles enabled for groups, adlists, and domain lists so replica-specific runtime/network settings are not clobbered.
  • keepalived.conf is generated and should stay local/untracked.