# 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 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. ## Components | Component | Where | Role | |-----------|-------|------| | 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 | ## How a name reaches a client ```mermaid sequenceDiagram participant C as LAN client (laptop, phone) participant G as G36 router (192.168.0.1) participant D as Pi-hole (192.168.0.143:53) participant P as Porkbun (WAN) participant X as Caddy (192.168.0.143:443) C->>G: DHCP request 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
(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 ``` 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. ## Pi-hole quadlet 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) - **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/pihole/addn-hosts` — `192.168.0.143 .franksdank.cloud` 2. Porkbun — A record ` 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`. See [[Pi-hole]] for the full configuration. ## G36 router DNS hand-off 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`):** 1. Open **Gateway → Connection → Local IP Network → IPv4**. 2. Uncheck **DNS Proxy** (disable the router DNS relay). 3. Set **DNS** to **Statically configured**. 4. Set **Primary DNS** to `192.168.0.143`. 5. Leave the secondary field empty or set `0.0.0.0`. 6. Apply, then re-lease DHCP on each client (`sudo dhclient -r && sudo dhclient`). The router admin interface has no programmatic API in this firmware. The web GUI is the only control path. Keep the credentials out of this repo; the repo tracks no router secrets. Verify after the change: ``` # on a LAN client resolv.conf # nameserver 192.168.0.143 dig romm.franksdank.cloud +short # expected: 192.168.0.143 ``` ## Dankclaw host DNS The Dankclaw host itself resolves the flat names through Pi-hole: - `/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). ## WAN records (Porkbun) The Porkbun zone `franksdank.cloud` carries an A record for every vhost plus the apex, all pointing at `71.227.227.172`. TTL is 300. The zone has no wildcard record: unknown subdomains return NXDOMAIN. Caddy's DNS-01 challenges add and remove `_acme-challenge.*` TXT records through the same API during issuance. See [[Caddy]] for the TLS flow. `franksdank.casa` is not in this Porkbun account. The `plex.franksdank.casa` vhost stays plain HTTP for that reason. The canonical Plex name is `plex.franksdank.cloud` (HTTPS). ## Failure modes | Symptom | First check | |---------|-------------| | 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 | 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 `). 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 Pi-hole and Caddy