caddy + pihole: make both boot-persistent and self-healing

Caddy left compose.yaml for a quadlet unit (caddy.service): joins
dankclaw_default for container-name upstreams, Pull=never for the local-only
image, Porkbun credentials via /home/dank/Containers/caddy/env written by
deploy from .env, bare-IP-catch-all health check. deploy_caddy installs,
enables, and starts the unit after retiring the bare container (state is in
host volumes).

Pi-hole: the repo now owns the sysctl drop-in
(dankclaw/pihole/sysctl/99-dnsmasq.conf, ip_unprivileged_port_start=53);
deploy_pihole installs it to /etc/sysctl.d/, stages conflicting later-sorted
drop-ins in the home trash, and applies 'sysctl --system' (the boot code
path). This fixes the 2026-08-26 crash loop (port-53 bind denied, restart
counter 1645) caused by a second drop-in resetting the value to 80.

Tests: new caddy_quadlet.bats seam; caddyfile/deployment_flow assertions
repointed from the removed compose service to the quadlet; pihole.bats pins
the sysctl drop-in and its deployment; test-changed.sh routes dankclaw/caddy/
and the pihole subtree to their seams. Docs: Caddy, Pi-hole, Dankclaw Stack,
work log.
This commit is contained in:
Frank
2026-08-26 19:52:57 -07:00
parent 66b58a127b
commit 8660209c35
14 changed files with 248 additions and 60 deletions
+33 -1
View File
@@ -305,7 +305,26 @@ deploy_caddy() {
# Retire the old split proxy before claiming ports 80/443. This is
# idempotent and leaves the old unit recoverable in the target trash.
retire_legacy_gitea_caddy
podman-compose -f dankclaw/compose.yaml up -d --no-deps caddy
# Caddy's env: Porkbun DNS-01 credentials from the checkout's .env. The
# live file holds secrets, so it exists only on the host; the quadlet
# reads it via EnvironmentFile.
mkdir -p /home/dank/Containers/caddy
if [[ ! -f /home/dank/Containers/caddy/env ]]; then
awk -F= '/^PORKBUN_API_KEY=|^PORKBUN_API_SECRET_KEY=/ { print }' \
dankclaw/.env > /home/dank/Containers/caddy/env
fi
local var
for var in PORKBUN_API_KEY PORKBUN_API_SECRET_KEY; do
grep -q "^${var}=." /home/dank/Containers/caddy/env \
|| { echo "caddy env missing ${var}; set it in dankclaw/.env" >&2; return 1; }
done
# Retire the bare compose container: all state (Caddyfile, certs, config)
# lives in host volumes, so removing the container loses nothing.
podman stop caddy >/dev/null 2>&1 || true
podman rm caddy >/dev/null 2>&1 || true
install_quadlet caddy dankclaw/caddy/quadlet caddy
systemctl --user enable caddy.service
systemctl --user restart caddy.service
if podman container exists caddy; then
local reloaded=0
for _ in 1 2 3 4 5; do
@@ -429,6 +448,19 @@ deploy_open_webui() {
}
deploy_pihole() {
# Rootless port 53 needs net.ipv4.ip_unprivileged_port_start <= 53, and it
# must hold at boot (systemd-sysctl applies /etc/sysctl.d). The repo
# drop-in is the single source of truth; a later-sorted file that resets
# the value to 80 took LAN DNS down after the 2026-08-26 reboot, so stage
# any such conflict in the home trash instead of deleting it.
run_privileged install -m 0644 dankclaw/pihole/sysctl/99-dnsmasq.conf \
/etc/sysctl.d/99-dnsmasq.conf
if grep -qs 'ip_unprivileged_port_start' /etc/sysctl.d/99-rootless-ports.conf; then
local sysctl_trash="$HOME/.AGENTTRASH/$(date +%Y%m%d)-sysctl-rootless-ports"
mkdir -p "$sysctl_trash"
run_privileged mv /etc/sysctl.d/99-rootless-ports.conf "$sysctl_trash/"
fi
run_privileged sysctl --system >/dev/null
mkdir -p /home/dank/Containers/pihole/etc-pihole /home/dank/Containers/pihole/etc-dockers
if [[ ! -f /home/dank/Containers/pihole/env ]]; then
cp dankclaw/pihole/env.example /home/dank/Containers/pihole/env
+11 -1
View File
@@ -104,6 +104,16 @@ for path in "${CHANGED[@]}"; do
add_test tests/searxng_quadlet.bats
add_test tests/deployment_scoping.bats
;;
# Caddy left compose.yaml on 2026-08-26 for a quadlet; caddyfile.bats
# now asserts against the quadlet itself. Route every change under
# dankclaw/caddy/ to both seams (this block must precede the
# dankclaw/*.container glob, which would otherwise swallow the unit).
dankclaw/caddy/*)
add_test tests/caddy_quadlet.bats
add_test tests/caddyfile.bats
add_test tests/docs_completeness.bats
add_test tests/deployment_scoping.bats
;;
dankclaw/*.container|dankclaw/*.network)
add_test tests/open_webui_quadlet.bats
add_test tests/pihole.bats
@@ -116,7 +126,7 @@ for path in "${CHANGED[@]}"; do
# their seam tests. The pihole-specific block must come first: shell
# case matches the first pattern, and dankclaw/pihole/quadlet/*.container
# also satisfies the dankclaw/*/quadlet/*.container fallback.
dankclaw/pihole/addn-hosts|dankclaw/pihole/env.example|dankclaw/pihole/quadlet/*.container|dankclaw/pihole/quadlet/*.network)
dankclaw/pihole/*)
add_test tests/pihole.bats
add_test tests/docs_completeness.bats
add_test tests/deployment_scoping.bats