Skip to content

๐Ÿ  Home Network: DDNS, DNS, and External Monitoring

๐Ÿ“Œ Purpose

This setup ensures:

  • Your home network is always reachable via a domain name
  • DNS updates automatically when your public IP changes
  • External access is continuously validated
  • Internal and external perspectives are intentionally separated

๐Ÿง  High-Level Architecture

Internet
  โ†“
Public DNS (Cloudflare / Google)
  โ†“
acorn.williatf.net (A record via DDNS)
  โ†“
Your Public IP (dynamic)
  โ†“
Router (port forwarding)
  โ†“
Nginx Proxy Manager
  โ†“
Internal Services (Home Assistant, etc.)

๐ŸŒ DNS Design

Split DNS Strategy (Intentional)

System DNS Used Behavior
Client devices Pi-hole Local overrides + ad blocking
Mac mini 1.1.1.1 External / real-world resolution

๐ŸŸข Pi-hole Configuration

Local DNS Records

macmini.lan โ†’ 192.168.5.189

Local CNAME Records

ha.williatf.net โ†’ macmini.lan
npm.williatf.net โ†’ macmini.lan
pihole.williatf.net โ†’ macmini.lan
...

Upstream DNS

  • Cloudflare โ†’ 1.1.1.1
  • Google โ†’ 8.8.8.8

Resolution Flow

Client โ†’ Pi-hole
       โ†’ local record? โ†’ return local IP
       โ†’ else โ†’ forward to upstream DNS

๐Ÿ”ต Mac Mini DNS (Important)

File:

/etc/resolv.conf

Contents:

nameserver 1.1.1.1

Why this is intentional

  • Bypasses Pi-hole completely
  • Ensures external DNS resolution
  • Prevents circular dependency (Pi-hole relying on itself)
  • Allows accurate external monitoring

๐Ÿ” Dynamic DNS (DDNS)

Method: cPanel Dynamic DNS

Uses a secure update URL:

https://williatf.net/cpanelwebcall/<token>

This updates:

acorn.williatf.net โ†’ current public IP

๐Ÿงพ DDNS Script

Location:

~/bin/update-home-ddns.sh

Responsibilities

  • Fetch current public IP
  • Compare with last known IP
  • Update DNS only if:
  • IP changed, or
  • 24 hours elapsed (forced refresh)

Script (reference)

#!/usr/bin/env bash
set -euo pipefail
umask 077

DDNS_URL="https://williatf.net/cpanelwebcall/<token>"
STATE_FILE="$HOME/.cache/home-ddns-ip.txt"
STAMP_FILE="$HOME/.cache/home-ddns-last-refresh.txt"
IP_SERVICE="https://api.ipify.org"
FORCE_AFTER_SECONDS=86400

mkdir -p "$(dirname "$STATE_FILE")"

CURRENT_IP="$(curl -s "$IP_SERVICE")"
LAST_IP="$(cat "$STATE_FILE" 2>/dev/null || true)"

NOW="$(date +%s)"
LAST_REFRESH="$(cat "$STAMP_FILE" 2>/dev/null || echo 0)"
AGE=$((NOW - LAST_REFRESH))

if [[ "$CURRENT_IP" != "$LAST_IP" || "$AGE" -ge "$FORCE_AFTER_SECONDS" ]]; then
  echo "Updating DDNS โ†’ $CURRENT_IP"
  curl -s "$DDNS_URL" >/dev/null
  echo "$CURRENT_IP" > "$STATE_FILE"
  echo "$NOW" > "$STAMP_FILE"
else
  echo "No change: $CURRENT_IP"
fi

โฑ๏ธ Automation (systemd)

Service

~/.config/systemd/user/home-ddns.service
[Unit]
Description=Update home Dynamic DNS

[Service]
Type=oneshot
ExecStart=/home/todd/bin/update-home-ddns.sh
StandardOutput=journal
StandardError=journal

Timer

~/.config/systemd/user/home-ddns.timer
[Unit]
Description=Run DDNS updater every 5 minutes

[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
Persistent=true

[Install]
WantedBy=timers.target

Enable

systemctl --user daemon-reload
systemctl --user enable --now home-ddns.timer
sudo loginctl enable-linger todd

๐Ÿ“ก External Monitoring (Uptime Kuma)

Tool

  • Uptime Kuma

Monitor Target

https://ha.williatf.net

What it verifies

End-to-end external path:

  • DNS resolution
  • DDNS correctness
  • Public IP routing
  • Router port forwarding
  • Nginx Proxy Manager
  • Backend service (Home Assistant)

Why this works

Even though Pi-hole has local overrides:

  • Mac mini uses public DNS
  • Domain resolves to public IP
  • Traffic exits network and re-enters (hairpin NAT)

This simulates real external access.


โš ๏ธ Important Behavior

Pi-hole vs Mac mini difference

Query Source Result
Pi-hole client ha.williatf.net โ†’ 192.168.x.x
Mac mini ha.williatf.net โ†’ public IP

This is intentional and correct.


๐Ÿงญ Design Decisions

Why not use Pi-hole on Mac mini

  • Avoids circular dependency
  • Keeps DNS working if Pi-hole fails
  • Enables true external monitoring

Tradeoff

Approach Result
Internal DNS Faster, but hides failures
External DNS Slightly slower, but accurate

Chosen: External DNS for correctness


๐Ÿงช Troubleshooting

Check DNS resolution

dig +short ha.williatf.net

Check Pi-hole resolution

dig @<pihole-ip> +short ha.williatf.net

Check public IP

curl -s https://api.ipify.org

Check DDNS logs

journalctl --user -u home-ddns.service -n 20 --no-pager

๐Ÿ” Security Notes

  • DDNS URL = secret (acts like an API key)
  • Regenerate it if exposed
  • Restrict script permissions

โœ… Final State

  • DDNS updates automatically
  • DNS resolves correctly internally and externally
  • External access is continuously monitored
  • System avoids circular dependencies

๐Ÿง  Future Reminder

The Mac mini intentionally uses public DNS (1.1.1.1).

This is required for accurate external monitoring.

Do not change this unless you understand the consequences.