Pi-hole swap: quadlet + env + records + Caddy vhost (retires dnsmasq)

- dankclaw/pihole/: pihole.container (quadlet, slirp4netns, 53+8083),
  env.example (ServerIP/TZ/DNS1/DNS2/WEBPASSWORD placeholders), addn-hosts
  (moved from dankclaw/dnsmasq/, + pihole.franksdank.cloud record)
- Caddyfile: pihole.franksdank.cloud vhost (family basicauth -> 8083)
- scripts/deploy-pihole-quadlet.sh: idempotent quadlet installer
- tests/pihole.bats replaces tests/dnsmasq.bats (same record validation
  + cross-checks, new quadlet/env seams); retired files in .AGENTTRASH/
This commit is contained in:
Frank committed 2026-08-24 13:54:35 -07:00
1 parent cf3da02af7
commit bb96fc55f7
9 files changed
+238 -91

No files matched your search

+10 -2
View File
@@ -7,8 +7,8 @@
# - dankserver services (192.168.0.235): by published LAN port
#
# Naming: flat franksdank.cloud scheme (see docs/Infrastructure/LAN-DNS.md).
# DNS for these names is served by the dnsmasq quadlet on this host
# (dankclaw/dnsmasq/addn-hosts — every name resolves to 192.168.0.143)
# DNS for these names is served by the Pi-hole quadlet on this host
# (dankclaw/pihole/addn-hosts — every name resolves to 192.168.0.143)
# and handed to LAN clients by the SURFboard G36 router.
#
# The basicauth hash below is a placeholder. deploy.sh renders the real
@@ -147,6 +147,14 @@ loki.franksdank.cloud {
reverse_proxy loki:3100
}
# --- Pi-hole (quadlet on slirp4netns; published 8083) ---
pihole.franksdank.cloud {
basicauth {
dankuser REPLACE_WITH_HASH
}
reverse_proxy 192.168.0.143:8083
}
# ═══════════════════════════════════════════════════════════════
# Services on dankserver (192.168.0.235) — published LAN ports
# ═══════════════════════════════════════════════════════════════
-6
View File
@@ -1,6 +0,0 @@
# Minimal dnsmasq image for the LAN DNS quadlet (dankclaw).
# Build on dankclaw: podman build -t localhost/dnsmasq:latest -f Containerfile .
FROM alpine:3.20
RUN apk add --no-cache dnsmasq
# -k keeps dnsmasq in the foreground (PID 1) so the container stays up.
CMD ["/usr/sbin/dnsmasq", "-k", "-C", "/etc/dnsmasq.conf"]
-24
View File
@@ -1,24 +0,0 @@
# dnsmasq — LAN DNS for the homelab.
#
# Runs in a rootless podman quadlet on dankclaw (192.168.0.143) with port 53
# published to the LAN. Serves the flat franksdank.cloud / franksdank.casa
# name scheme from addn-hosts (one A record per Caddy vhost); everything else
# goes to the pinned upstream resolvers. The SURFboard G36 router hands this
# server out to DHCP clients, so LAN devices resolve service names without
# per-device config. See docs/Infrastructure/LAN-DNS.md.
# The slirp netns has no useful /etc/resolv.conf; pin upstreams instead.
no-resolv
server=1.1.1.1
server=1.0.0.1
# Local service names (must stay in sync with the Caddyfile vhosts).
addn-hosts=/etc/dnsmasq/addn-hosts
# Never resolve single-label names or private/bogus ranges as public names.
domain-needed
bogus-priv
# Cache.
cache-size=1000
neg-ttl=300
-33
View File
@@ -1,33 +0,0 @@
# Quadlet unit: dnsmasq LAN DNS (rootless, boot-persistent).
#
# Install path on dankclaw: ~/.config/containers/systemd/dnsmasq.container
# Unit name: dnsmasq.service (container name: dnsmasq)
#
# The LAN's authoritative DNS for the flat franksdank.cloud / franksdank.casa
# name scheme. It serves A records from addn-hosts (one per Caddy vhost) and
# forwards everything else to pinned upstream resolvers. The SURFboard G36
# router hands this server out to DHCP clients (DNS Relay -> statically
# configured -> 192.168.0.143), so no client needs per-device config.
#
# Runs on slirp4netns with port 53 published to the LAN (the same pattern as
# open-webui). Config mounts from /home/dank/Containers/dnsmasq. See
# docs/Infrastructure/LAN-DNS.md.
[Unit]
Description=dnsmasq LAN DNS container
After=network.target
[Container]
ContainerName=dnsmasq
Network=slirp4netns
Image=localhost/dnsmasq:latest
PublishPort=53:53/tcp
PublishPort=53:53/udp
Volume=/home/dank/Containers/dnsmasq/dnsmasq.conf:/etc/dnsmasq.conf:ro
Volume=/home/dank/Containers/dnsmasq/addn-hosts:/etc/dnsmasq/addn-hosts:ro
[Service]
Restart=always
[Install]
WantedBy=default.target
@@ -1,9 +1,10 @@
# A records for the flat franksdank.name scheme (dnsmasq addn-hosts format:
# <IP> <name> [<name> ...]).
# <IP> <name> [<name> ...]). Served by the Pi-hole quadlet (FTL is a
# dnsmasq fork): the quadlet mounts this file into /etc/dnsmasq.d/ read-only.
#
# Caddy is consolidated on dankclaw (192.168.0.143): it fronts every service
# on both hosts, so every name resolves to that one IP. Keep in sync with the
# Caddyfile (tests/dnsmasq.bats cross-checks this).
# Caddyfile (tests/pihole.bats cross-checks this).
#
# 192.168.0.143 = dankclaw (runs the Caddy container + the AI/dev stack)
# 192.168.0.235 = dankserver (media stack; reached by Caddy via published
@@ -36,3 +37,4 @@
192.168.0.143 grafana.franksdank.cloud
192.168.0.143 prometheus.franksdank.cloud
192.168.0.143 loki.franksdank.cloud
192.168.0.143 pihole.franksdank.cloud
+18
View File
@@ -0,0 +1,18 @@
# Pi-hole container environment (dankclaw).
# Copy to /home/dank/Containers/pihole/env and fill real values.
# The real env file is never committed (it holds the web UI password).
# The host IP the container answers for (also shown in the web UI).
ServerIP=192.168.0.143
# Timezone for the web UI and logs.
TZ=America/Los_Angeles
# Pinned upstream resolvers. The slirp netns has no useful resolv.conf, so
# pin them (mirrors the retired dnsmasq.conf server= lines).
DNS1=1.1.1.1
DNS2=1.0.0.1
# Web UI password (the shared family password; the same credential as
# CADDY_BASICAUTH_HASH in the host .env).
WEBPASSWORD=changeme
+42
View File
@@ -0,0 +1,42 @@
# Quadlet unit: Pi-hole LAN DNS (rootless, boot-persistent).
#
# Install path on dankclaw: ~/.config/containers/systemd/pihole.container
# Unit name: pihole.service (container name: pihole)
#
# The LAN's authoritative DNS for the flat franksdank.cloud / franksdank.casa
# name scheme. It retired the dnsmasq quadlet (2026-08-24): Pi-hole FTL is a
# dnsmasq fork, so the addn-hosts record file carries over unchanged and is
# mounted into /etc/dnsmasq.d/ (the FTL dnsmasq config dir). It serves one A
# record per Caddy vhost and forwards everything else to the pinned upstream
# resolvers (DNS1/DNS2 in the env file). The SURFboard G36 router hands this
# server out to DHCP clients (DNS statically configured -> 192.168.0.143),
# so no client needs per-device config.
#
# Runs on slirp4netns with port 53 (DNS) and 8083 (web UI) published to the
# LAN (the same pattern as open-webui). Config + state mount from
# /home/dank/Containers/pihole. The web UI password is WEBPASSWORD in the env
# file. See docs/Infrastructure/LAN-DNS.md.
[Unit]
Description=Pi-hole LAN DNS container
After=network-online.target
Wants=network-online.target
[Container]
ContainerName=pihole
Image=docker.io/pihole/pihole:latest
Network=slirp4netns
PublishPort=0.0.0.0:53:53/udp
PublishPort=0.0.0.0:53:53/tcp
PublishPort=0.0.0.0:8083:80/tcp
EnvironmentFile=/home/dank/Containers/pihole/env
Volume=/home/dank/Containers/pihole/etc-pihole:/etc/pihole
Volume=/home/dank/Containers/pihole/etc-dockers:/etc/dockers
Volume=/home/dank/Containers/pihole/addn-hosts:/etc/dnsmasq.d/addn-hosts:ro
[Service]
Restart=always
RestartSec=5
[Install]
WantedBy=default.target