jerry@homelab:~/docs$ cat network.md
DNS, Docker Networking & the Tailscale Mesh
DNS and Docker networking are the plumbing everything else in the homelab sits on top of. This is a deeper look at how split-horizon DNS, the shared proxy Docker network, and the Tailscale mesh fit together underneath the zero-trust model described in Architecture.
Split-Horizon DNS (Pi-hole)
Every *.kroskinski.com subdomain resolves two different ways depending on who's asking. On the LAN or over Tailscale, a Pi-hole local DNS record resolves it straight to the reverse proxy. From the open internet, a public TransIP A record does the same job. Same hostname, same destination, different resolver -- nothing internal is ever reachable by the public path.
Pi-hole itself isn't a single point of failure: a second instance runs on a spare Raspberry Pi 3, kept in sync hourly via Nebula Sync against Pi-hole v6's Teleporter API -- adlists, whitelist/blacklist, and local DNS records all mirror across, so both instances agree on where every subdomain points. Both Pi-hole Tailscale IPs are registered as tailnet nameservers, and the router's DHCP hands out the backup as secondary DNS, so it's a genuine failover path, not just an idle mirror.
Docker Networking: the proxy Network
Every web-facing stack attaches to one shared external network, proxy, so Caddy can reach any app by container name. Caddy itself owns the host's LAN and Tailscale IPs on ports 80/443 -- nothing else on the box gets to bind there, and nothing outside Docker's internal networking touches another container directly.
- Container-to-container traffic never round-trips through Caddy. Anything that needs to talk to another service on the same host does it by container name over
proxy(or a dedicated shared network), never by hitting the host's own LAN IP. - Databases stay isolated. Postgres and Redis containers sit on private per-stack networks that Caddy -- and every other app -- simply can't reach.
Gotcha: the bare-IP trap. A sync job once pointed at the primary Pi-hole's LAN IP directly, expecting to reach Pi-hole. That IP belongs to Caddy, not Pi-hole -- Pi-hole's own port isn't published to the host. The request hit Caddy instead, got redirected to HTTPS, and failed TLS with a cryptic tls: internal error, since Caddy had no Host header to route a bare-IP request against. Fix: address Pi-hole by container name on its own Docker network, and never treat a host's LAN/Tailscale IP as a shortcut to "whatever's listening on port 80" -- that IP belongs to Caddy alone.
Tailscale Mesh
Right now, Tailscale connects individual hosts -- the two Intel NUCs, both Oracle Cloud VMs, the Raspberry Pi -- as their own nodes, each reachable only for the specific subdomains published through Caddy. Full LAN subnet routing is planned but deliberately not turned on yet: advertising the whole home network as a Tailscale route would give any tailnet-connected device direct IP access to everything on it, printers and router admin pages included, not just the curated subdomain list. That's a real widening of the trust boundary, worth pairing with Tailscale ACLs before flipping on -- not something to enable just because it's convenient.
Known gap: the backup Pi-hole's subdomain doesn't yet resolve correctly from the external monitoring instance, even though it resolves fine everywhere else. Still under investigation -- likely a missing public DNS record or Caddy site block, not a Tailscale or Authelia issue.
jerry@homelab:~/docs$ █
cd ..