docs: Pi-hole KB page + dnsmasq retirement across the vault
- new docs/Infrastructure/Pi-hole.md (quadlet, records, upstream, web UI, failure modes incl. the rootless /etc/hosts single-label echo) - LAN-DNS.md: Pi-hole quadlet section, router hand-off, host DNS - Network Architecture / Caddy / Homepage / index / AGENTS.md: name swap
This commit is contained in:
@@ -11,8 +11,8 @@ one checkout per host, one deploy script.
|
||||
`frank-default-small` LLM service.
|
||||
- **Dankclaw** (192.168.0.143) — AI/search/development/observability stack
|
||||
(SearXNG via Gluetun, ComfyUI, OpenWebUI, Hermes, Gitea, Grafana, Prometheus,
|
||||
Loki, Alloy) plus the `frank-default-large` LLM service. Caddy on Dankclaw
|
||||
reverse-proxies both hosts.
|
||||
Loki, Alloy, Pi-hole) plus the `frank-default-large` LLM service. Caddy on
|
||||
Dankclaw reverse-proxies both hosts.
|
||||
|
||||
**2026-08-22 merge:** the former `vllmScripts` repository (LLM fleet scripts,
|
||||
units, env tunables, and runbook) is merged into this repo. Its full git
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Network Architecture
|
||||
|
||||
One Caddy fronts both hosts. It runs as a container on Dankclaw (192.168.0.143) and terminates TLS for every service on the LAN. dnsmasq on Dankclaw answers the service names for LAN clients; Porkbun answers them for the WAN. The G36 router firewall governs external access. SearXNG outbound traffic exits through the Gluetun VPN container.
|
||||
One Caddy fronts both hosts. It runs as a container on Dankclaw (192.168.0.143) and terminates TLS for every service on the LAN. Pi-hole on Dankclaw answers the service names for LAN clients; Porkbun answers them for the WAN. The G36 router firewall governs external access. SearXNG outbound traffic exits through the Gluetun VPN container.
|
||||
|
||||
## Topology
|
||||
|
||||
@@ -12,7 +12,7 @@ graph TB
|
||||
EXT -->|port forwards| C
|
||||
subgraph Dankclaw[192.168.0.143]
|
||||
C[Caddy :80 :443]
|
||||
D[dnsmasq :53]
|
||||
D[Pi-hole :53]
|
||||
C -->|comfyui:8188| F[ComfyUI]
|
||||
C -->|192.168.0.143:3000| G[Gitea quadlet]
|
||||
C -->|192.168.0.143:8081| SE[SearXNG quadlet]
|
||||
@@ -35,7 +35,7 @@ Dankserver runs no proxy of its own. Its services expose published LAN ports, an
|
||||
|
||||
| Scope | Convention | Resolved by |
|
||||
|-------|------------|-------------|
|
||||
| LAN | `romm.franksdank.cloud` → 192.168.0.143 | dnsmasq (`addn-hosts`) |
|
||||
| LAN | `romm.franksdank.cloud` → 192.168.0.143 | Pi-hole (`addn-hosts`) |
|
||||
| WAN | `romm.franksdank.cloud` → 71.227.227.172 | Porkbun A record |
|
||||
|
||||
Users type a name and nothing else. Caddy maps the name to the upstream. The split between the two resolvers removes the need for router NAT loopback. See [[LAN-DNS]] for the flow and the router hand-off.
|
||||
@@ -56,7 +56,7 @@ SearXNG attaches to the Gluetun network namespace (`network_mode: service:gluetu
|
||||
|
||||
## See Also
|
||||
|
||||
- [[LAN-DNS]] — name resolution, dnsmasq, router hand-off
|
||||
- [[LAN-DNS]] — name resolution, Pi-hole, router hand-off
|
||||
- [[Caddy]] — vhost map, TLS, basic auth
|
||||
- [[Dankserver Stack]], [[Dankclaw Stack]]
|
||||
- [[Gluetun]]
|
||||
|
||||
@@ -85,8 +85,8 @@ Certificates and ACME state live in the caddy data volume. Back it up with the s
|
||||
|
||||
- `admin off` disables the config file watcher: rendered changes then sit on disk while Caddy serves its stale in-memory config until the container is recreated, and nothing logs an error (2026-08-22: a broken basicauth hash survived several deploys this way). `tests/caddyfile.bats` forbids `admin off`.
|
||||
- The basicauth template must stay a bare `REPLACE_WITH_HASH` token (no `$2a$14$` prefix) — the rendered value is the complete hash. A double prefix silently rejects every password with 401; no Caddy error is logged.
|
||||
- A new vhost needs three things: a dnsmasq `addn-hosts` line, a Porkbun A record, and the Caddyfile block. See [[LAN-DNS]].
|
||||
- The G36 has no NAT loopback, so LAN clients must resolve the name to the LAN IP. That is the dnsmasq role in [[LAN-DNS]].
|
||||
- A new vhost needs three things: a Pi-hole `addn-hosts` line, a Porkbun A record, and the Caddyfile block. See [[LAN-DNS]].
|
||||
- The G36 has no NAT loopback, so LAN clients must resolve the name to the LAN IP. That is the Pi-hole role in [[LAN-DNS]].
|
||||
- Caddy serves HTTPS on 443 and HTTP on 80 (redirects). The router needs no 80/443 port forward for LAN use.
|
||||
|
||||
## See Also
|
||||
|
||||
@@ -81,7 +81,7 @@ flowchart TD
|
||||
|
||||
**User services** (root page): SearXNG, ComfyUI, OpenWebUI, Gitea, Calibre-Web, RomM, Copyparty, Plex, Jellyfin.
|
||||
|
||||
**Admin services** (`/admin/`): Hermes, Grafana, Prometheus, Loki, Caddy, dnsmasq, Gluetun, the LLM fleet, and the low-level backends (mariadb, redis, Postgres, node-exporter, alloy). The backends show status only. They get a link only where a UI is meaningful.
|
||||
**Admin services** (`/admin/`): Hermes, Grafana, Prometheus, Loki, Caddy, Pi-hole, Gluetun, the LLM fleet, and the low-level backends (mariadb, redis, Postgres, node-exporter, alloy). The backends show status only. They get a link only where a UI is meaningful.
|
||||
|
||||
**Excluded for now:** the *arr stack (Radarr, Sonarr, Lidarr, Readarr, Prowlarr, qBittorrent). It is not started. It is a separate effort, possibly on a different host.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# LAN DNS
|
||||
|
||||
The homelab gives every service a name under `franksdank.cloud`. Users type a name; they never type a port number. This page covers how names reach users: the dnsmasq server, the G36 router hand-off, and the split between LAN and WAN resolution.
|
||||
The homelab gives every service a name under `franksdank.cloud`. Users type a name; they never type a port number. This page covers how names reach users: the Pi-hole server, the G36 router hand-off, and the split between LAN and WAN resolution.
|
||||
|
||||
See [[Caddy]] for the reverse proxy that serves the names, and [[Network Architecture]] for the full topology.
|
||||
|
||||
@@ -8,7 +8,7 @@ See [[Caddy]] for the reverse proxy that serves the names, and [[Network Archite
|
||||
|
||||
| Component | Where | Role |
|
||||
|-----------|-------|------|
|
||||
| dnsmasq | Dankclaw quadlet (`dankclaw/dnsmasq/`) | Answers the flat service names for LAN clients |
|
||||
| Pi-hole | Dankclaw quadlet (`dankclaw/pihole/`) | Answers the flat service names for LAN clients |
|
||||
| SURFboard G36 | 192.168.0.1 | Hands dnsmasq to clients as the DHCP DNS server |
|
||||
| Porkbun | Registrar for `franksdank.cloud` | Answers the same names for the WAN |
|
||||
| Caddy | Dankclaw (192.168.0.143:443) | Serves every name over HTTPS |
|
||||
@@ -19,7 +19,7 @@ See [[Caddy]] for the reverse proxy that serves the names, and [[Network Archite
|
||||
sequenceDiagram
|
||||
participant C as LAN client (laptop, phone)
|
||||
participant G as G36 router (192.168.0.1)
|
||||
participant D as dnsmasq (192.168.0.143:53)
|
||||
participant D as Pi-hole (192.168.0.143:53)
|
||||
participant P as Porkbun (WAN)
|
||||
participant X as Caddy (192.168.0.143:443)
|
||||
|
||||
@@ -27,42 +27,41 @@ sequenceDiagram
|
||||
G-->>C: lease + DNS = 192.168.0.143
|
||||
C->>D: dig romm.franksdank.cloud
|
||||
D-->>C: 192.168.0.143
|
||||
Note over D: addn-hosts answer<br/>(dankclaw/dnsmasq/addn-hosts)
|
||||
Note over D: addn-hosts answer<br/>(dankclaw/pihole/addn-hosts)
|
||||
C->>X: https://romm.franksdank.cloud
|
||||
X-->>C: RomM (192.168.0.235:8085 upstream)
|
||||
C->>P: dig franksdank.cloud (unknown name)
|
||||
P-->>C: 71.227.227.172
|
||||
```
|
||||
|
||||
dnsmasq answers only the names in `addn-hosts`. Every other query goes to the upstream resolvers `1.1.1.1` and `1.0.0.1`. The zone apex and unknown subdomains fall through to Porkbun. This avoids a wildcard override.
|
||||
Pi-hole answers only the names in `addn-hosts`. Every other query goes to the upstream resolvers `1.1.1.1` and `1.0.0.1`. The zone apex and unknown subdomains fall through to Porkbun. This avoids a wildcard override.
|
||||
|
||||
## Why split DNS
|
||||
|
||||
The G36 has no NAT loopback (hairpin). A LAN client that resolves a name to the WAN IP and then connects to it would time out. Split DNS removes the hairpin: LAN clients get the LAN IP, WAN clients get the public IP. Both use the same name and the same Let's Encrypt certificate. See [[Caddy]] for the TLS side.
|
||||
|
||||
## dnsmasq quadlet
|
||||
## Pi-hole quadlet
|
||||
|
||||
- **Location:** `dankclaw/dnsmasq/` — `dnsmasq.container` (quadlet), `dnsmasq.conf`, `addn-hosts`, `Containerfile`
|
||||
- **Port:** 53/udp + 53/tcp on 192.168.0.143
|
||||
Pi-hole FTL is a dnsmasq fork. It serves the same `addn-hosts` file the retired dnsmasq quadlet used, mounted into `/etc/dnsmasq.d/` read-only. It retired the dnsmasq quadlet on 2026-08-24.
|
||||
|
||||
- **Location:** `dankclaw/pihole/` — `quadlet/pihole.container`, `env.example`, `addn-hosts`
|
||||
- **Ports:** 53/udp + 53/tcp (DNS) and 8083/tcp (web UI) on 192.168.0.143
|
||||
- **Host prerequisite 1:** `sysctl net.ipv4.ip_unprivileged_port_start=53` (persisted in `/etc/sysctl.d/99-dnsmasq.conf`)
|
||||
- **Host prerequisite 2:** `DNSStubListener=no` in `/etc/systemd/resolved.conf` (the resolved stub would otherwise hold port 53 inside the container netns)
|
||||
- **Rebuild + restart after a change:**
|
||||
```
|
||||
podman build -t dnsmasq:latest -f dankclaw/dnsmasq/Containerfile .
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart dnsmasq
|
||||
```
|
||||
- **Env file:** `/home/dank/Containers/pihole/env` (never committed) — `ServerIP`, `TZ`, `DNS1` + `DNS2` (pinned upstreams `1.1.1.1` + `1.0.0.1`), `WEBPASSWORD` (the shared family password)
|
||||
- **Web UI:** `pihole.franksdank.cloud` (Caddy vhost → 8083), family login
|
||||
- **Deploy + restart after a change:** `./scripts/deploy-pihole-quadlet.sh dankclaw`
|
||||
|
||||
Add a new service name in two places:
|
||||
|
||||
1. `dankclaw/dnsmasq/addn-hosts` — `192.168.0.143 <name>.franksdank.cloud`
|
||||
1. `dankclaw/pihole/addn-hosts` — `192.168.0.143 <name>.franksdank.cloud`
|
||||
2. Porkbun — A record `<name> 71.227.227.172` (so WAN clients and the DNS-01 challenge work)
|
||||
|
||||
Then add the vhost to [[Caddy]] and run `./scripts/deploy.sh dankclaw`.
|
||||
Then add the vhost to [[Caddy]] and run `./scripts/deploy.sh dankclaw`. See [[Pi-hole]] for the full configuration.
|
||||
|
||||
## G36 router DNS hand-off
|
||||
|
||||
The router must tell DHCP clients to use dnsmasq. Until that step is done, new clients get the router's own DNS relay and the flat names do not resolve for them. Existing clients that already use dnsmasq are unaffected.
|
||||
The router must tell DHCP clients to use Pi-hole. Until that step is done, new clients get the router's own DNS relay and the flat names do not resolve for them. Existing clients that already use Pi-hole are unaffected.
|
||||
|
||||
**Manual step (web GUI at `https://192.168.0.1`):**
|
||||
|
||||
@@ -86,9 +85,9 @@ dig romm.franksdank.cloud +short
|
||||
|
||||
## Dankclaw host DNS
|
||||
|
||||
The Dankclaw host itself resolves the flat names through dnsmasq:
|
||||
The Dankclaw host itself resolves the flat names through Pi-hole:
|
||||
|
||||
- `/etc/systemd/resolved.conf`: `DNS=192.168.0.143 192.168.0.1` (dnsmasq first, router as fallback)
|
||||
- `/etc/systemd/resolved.conf`: `DNS=192.168.0.143 192.168.0.1` (Pi-hole first, router as fallback)
|
||||
|
||||
Without this, the host would resolve the names through the router to the WAN IP and time out (no hairpin).
|
||||
|
||||
@@ -104,12 +103,14 @@ The Porkbun zone `franksdank.cloud` carries an A record for every vhost plus the
|
||||
|---------|-------------|
|
||||
| Name does not resolve on a new client | `resolv.conf` shows 192.168.0.1, not .143 — the G36 hand-off step is missing |
|
||||
| Name resolves to 71.227.x and times out on the LAN | Client used the router DNS — same root cause |
|
||||
| `dig @192.168.0.143` fails entirely | dnsmasq quadlet down: `systemctl --user status dnsmasq` |
|
||||
| `dig @192.168.0.143` fails entirely | Pi-hole quadlet down: `systemctl --user status pihole` |
|
||||
| Single-label name (for example `dankclaw`) answers `127.0.1.1` | The rootless container `/etc/hosts` carries the host entry (`127.0.1.1 <host>`). FTL serves it. Use the FQDN, or an `/etc/hosts` entry on the client |
|
||||
| HTTPS works from the host but not the WAN | No port forward on the G36 for that service (manual router step) |
|
||||
| Cert renewal fails in Caddy logs | Porkbun API credentials in the Dankclaw `.env`; test with the Porkbun API `/ping` endpoint |
|
||||
|
||||
## See Also
|
||||
|
||||
- [[Pi-hole]] — the LAN DNS server (quadlet, records, web UI)
|
||||
- [[Caddy]] — TLS, vhost list, DNS-01
|
||||
- [[Network Architecture]] — topology, access policy
|
||||
- [[Dankclaw Stack]] — the host that carries dnsmasq and Caddy
|
||||
- [[Dankclaw Stack]] — the host that carries Pi-hole and Caddy
|
||||
@@ -0,0 +1,63 @@
|
||||
# Pi-hole
|
||||
|
||||
Pi-hole is the LAN DNS server for the homelab. It runs as a quadlet container on Dankclaw (192.168.0.143). It answers the flat `franksdank.cloud` / `franksdank.casa` service names and filters ad traffic on the LAN. It retired the dnsmasq quadlet on 2026-08-24. See [[LAN-DNS]] for the name-resolution flow and the router hand-off.
|
||||
|
||||
## Components
|
||||
|
||||
| Component | Where | Role |
|
||||
|-----------|-------|------|
|
||||
| `pihole` quadlet | `dankclaw/pihole/quadlet/pihole.container` | Boot-persistent container unit |
|
||||
| FTL | `docker.io/pihole/pihole:latest` | DNS engine (a dnsmasq fork) + ad blocking |
|
||||
| `addn-hosts` | `dankclaw/pihole/addn-hosts` | One A record per Caddy vhost |
|
||||
| env file | `/home/dank/Containers/pihole/env` (host) | `ServerIP`, `TZ`, `DNS1`, `DNS2`, `WEBPASSWORD` |
|
||||
| Web UI | `pihole.franksdank.cloud` (Caddy → 8083) | Dashboard + admin, family login |
|
||||
|
||||
## Quadlet
|
||||
|
||||
- **Install path on Dankclaw:** `~/.config/containers/systemd/pihole.container` (unit `pihole.service`)
|
||||
- **Network:** `slirp4netns` (the same pattern as open-webui and gitea)
|
||||
- **Published ports:** 53/udp + 53/tcp (DNS), 8083/tcp (web UI)
|
||||
- **Mounts:**
|
||||
- `/home/dank/Containers/pihole/etc-pihole` → `/etc/pihole` (config + state, preserved across restarts)
|
||||
- `/home/dank/Containers/pihole/etc-dockers` → `/etc/dockers` (blocklists)
|
||||
- `/home/dank/Containers/pihole/addn-hosts` → `/etc/dnsmasq.d/addn-hosts` (read-only; the record file from this repo)
|
||||
- **Restart:** `Restart=always`, `RestartSec=5`
|
||||
- **Deploy:** `./scripts/deploy-pihole-quadlet.sh dankclaw` (idempotent; preserves the data dirs)
|
||||
|
||||
## Records
|
||||
|
||||
The quadlet mounts `dankclaw/pihole/addn-hosts` into the container's dnsmasq config dir. FTL reads it and answers one A record per Caddy vhost, all pointing at `192.168.0.143`. The file stays in sync with the Caddyfile (`tests/pihole.bats` cross-checks the two). Add a name there and in Porkbun, then deploy. See [[LAN-DNS]] for the two-place rule.
|
||||
|
||||
## Upstream
|
||||
|
||||
The slirp netns has no useful `resolv.conf`. The env file pins the upstreams: `DNS1=1.1.1.1`, `DNS2=1.0.0.1` (mirrors the retired `dnsmasq.conf` `server=` lines). Every query that is not a local record goes to those resolvers. The zone apex and unknown subdomains fall through to Porkbun.
|
||||
|
||||
## Web UI and API
|
||||
|
||||
- **URL:** `https://pihole.franksdank.cloud/` (Caddy vhost, family basicauth → 8083)
|
||||
- **Password:** `WEBPASSWORD` in the env file (the shared family password, the same credential as `CADDY_BASICAUTH_HASH`)
|
||||
- **API:** `https://pihole.franksdank.cloud/api/...` — authenticate with the session cookie, then query the blocklist, the local DNS records, and the query log
|
||||
- **Port on the LAN:** `http://192.168.0.143:8083/` (direct, no Caddy)
|
||||
|
||||
## Host prerequisites
|
||||
|
||||
1. `sysctl net.ipv4.ip_unprivileged_port_start=53` (persisted in `/etc/sysctl.d/99-dnsmasq.conf`) — rootless Podman may then publish port 53.
|
||||
2. `DNSStubListener=no` in `/etc/systemd/resolved.conf` — the resolved stub would otherwise hold port 53 inside the container netns.
|
||||
|
||||
Both prerequisites survive the dnsmasq → Pi-hole swap. Do not remove them.
|
||||
|
||||
## Failure modes
|
||||
|
||||
| Symptom | First check |
|
||||
|---------|-------------|
|
||||
| `dig @192.168.0.143` fails entirely | Quadlet down: `systemctl --user status pihole` |
|
||||
| A name does not resolve for a new client | The client's `resolv.conf` does not show `192.168.0.143` — the G36 hand-off is missing. See [[LAN-DNS]] |
|
||||
| The web UI asks for a password loop | `WEBPASSWORD` in the env file is the placeholder `changeme` — fill the real value, restart the unit |
|
||||
| Upstream queries fail (local names still answer) | `DNS1`/`DNS2` in the env file are missing or wrong |
|
||||
| Single-label name (for example `dankclaw`) answers `127.0.1.1` | The rootless container `/etc/hosts` carries the host entry (`127.0.1.1 <host>`), and FTL serves it. Use the FQDN, or add an `/etc/hosts` entry on the client |
|
||||
|
||||
## See Also
|
||||
|
||||
- [[LAN-DNS]] — name resolution flow, router hand-off, split DNS
|
||||
- [[Caddy]] — the `pihole.franksdank.cloud` vhost
|
||||
- [[Dankclaw Stack]] — the host that carries Pi-hole
|
||||
+2
-1
@@ -62,7 +62,8 @@ covers both workloads).
|
||||
|
||||
## Infrastructure
|
||||
|
||||
- [[LAN-DNS]] — name resolution, dnsmasq, router DNS hand-off
|
||||
- [[LAN-DNS]] — name resolution, Pi-hole, router DNS hand-off
|
||||
- [[Pi-hole]] — the LAN DNS server (quadlet, records, web UI)
|
||||
- [[Caddy]] — reverse proxy and TLS
|
||||
- [[Homepage]] — password-protected service index
|
||||
- [[Homepage Design Grilling]] — homepage decision log and open questions
|
||||
|
||||
Reference in New Issue
Block a user