Skip to main content

Pi-hole

Network-wide DNS ad blocker and optional DHCP server. Blocks ads, trackers, and malicious domains for all devices on the network by acting as the primary DNS resolver.

Ports​

ServicePort
DNS53 (TCP + UDP) — published on the host
Web UI80 (container-internal), reached only through Caddy at https://pihole.<domain>
Web UI direct (optional)host 42480→80, 42443→443 — uncomment in the compose template to bypass Caddy
DHCP (optional)67 — uncomment in the compose template
NTP (optional)123 — uncomment in the compose template

Setup​

  1. Enable it in .env.shared:
    EXIST_IS_HOSTING_PIHOLE=true
  2. Render and start, from the repo root:
    ./existential.sh && docker compose up -d
  3. Point your router's DNS to the host running Pi-hole

Pi-hole then resolves the whole of EXIST_DOMAIN locally via a single wildcard record, which removes the stack's dependency on public DNS. It is an upgrade, not a requirement — see How It Works.

Upstream resolution and blocklists​

Anything not covered by the wildcard record or a blocklist is forwarded upstream. Out of the box that's Google DNS (8.8.8.8, 8.8.4.4) — the image's own default, no config needed. Override it with FTLCONF_dns_upstreams in docker-compose.exist.yml if you'd rather use another resolver (e.g. Cloudflare's 1.1.1.1).

The container ships its own cron, so blocklists (gravity) update weekly, Sunday 04:15 (container time — see PIHOLE_TZ) with no setup required; the query log flushes daily at midnight. Trigger an update manually with docker exec pihole pihole -g, or manage blocklists at https://pihole.<domain>/admin/lists.

Debugging​

docker compose logs pihole
# Check DNS resolution
docker exec pihole nslookup example.com