Networking Ddns
Dynamic DNS (DDNS) — Direct-to-Home Networking
How
home.alienzj.orgstays pointed at a dynamic residential IP using Cloudflare DDNS, and why you might (or might not) want to bypass the VPS.
Concepts
IP Types
| Type | Example | Who gets it |
|---|---|---|
| Static public | VPS IP | Datacenter providers, business ISPs |
| Dynamic public | 198.51.100.1 → 203.0.113.1 | Residential ISPs, rotates periodically |
| CGNAT | 100.64.x.x (RFC 6598) | Mobile ISPs, some residential ISPs |
| Private | 192.168.x.x, 10.x.x.x | Behind a NAT router |
This home network has dynamic public IPs — a pool of real public addresses
rotated by the ISP. Single NAT layer (the router at 192.168.31.1). Not CGNAT.
How ISPs Assign IPs
ISP edge router
├── Pool: 203.0.113.0/24, 198.51.100.0/24, ...
│ ├── id3-eniac gets 203.0.113.1 (today)
│ └── lab-matrix gets 198.51.100.1 (today)
│ └── (tomorrow: both may rotate to different IPs)
└── Router (192.168.31.1) — single NAT, not CGNAT
Two Paths to Home
Path A: Via VPS (current default)
Internet → vps-pacman (static IP, nginx) → Tailscale → lab-matrix (100.88.42.65)
↑ encrypted overlay
| Pros | Cons |
|---|---|
| Static IP, no DDNS needed | Extra latency (VPS hop) |
| VPS filters malicious traffic | VPS bandwidth costs |
| TLS terminates at VPS | VPS is a single point |
| No home IP exposure |
Path B: Direct-to-Home (DDNS)
Internet → home.alienzj.org (dynamic IP, Cloudflare DDNS) → router → host
↑ updated every 5min by cloudflare-ddns service
| Pros | Cons |
|---|---|
| Zero VPS latency | Dynamic IP — stale DNS during rotation window |
| No VPS bandwidth costs | Exposes home IP to DNS |
| Direct path, fewer hops | Needs port forwarding on router |
| TLS certs must be on the home host |
Path C: Hybrid (best of both)
home.alienzj.org → DDNS → home IP (direct, low latency)
*.alienzj.org → VPS → Tailscale → lab-matrix (existing infra)
Direct-to-home for latency-sensitive services; VPS for everything else.
Module: modules/services/net/ddns.nix
Options
modules.services.net.ddns = {
enable = true; # toggle the DDNS updater
domain = "home.alienzj.org"; # DNS record to update
ip4Provider = "cloudflare.trace"; # IP detection method
ip6Provider = "none"; # IPv6 disabled (no home IPv6)
updateCron = "@every 5m"; # update frequency
proxied = false; # Cloudflare proxy (orange cloud)
};
How it works
cloudflare-ddnsservice starts at boot (afternetwork-online.target)- Reads
CLOUDFLARE_API_TOKENfrom agenix secret (/run/agenix/cloudflare-api-token) - Detects public IP via
cloudflare.trace(Cloudflare’s/cdn-cgi/traceendpoint) - Updates the
home.alienzj.orgA record via Cloudflare API - Repeats every 5 minutes (configurable via
UPDATE_CRON)
Per-host enablement
DDNS should only be enabled on the host that owns home.alienzj.org:
# hosts/id3-eniac/modules/modules.nix
modules.services.net.ddns.enable = true;
Prerequisites
- Cloudflare API token with
Zone.DNS.Editpermission (already exists for ACME) - DNS record
home.alienzj.orgmust exist in Cloudflare (even as a dummy) - Port forwarding on the router if serving traffic:
- TCP 443 → host IP (nginx/https)
- TCP 80 → host IP (ACME HTTP-01, optional)
IP Detection Methods
cloudflare-ddns supports multiple detection providers, configured via ip4Provider:
| Provider | How it works | Privacy |
|---|---|---|
cloudflare.trace | Parses /cdn-cgi/trace | Cloudflare already sees your DNS |
cloudflare.doh | DNS-over-HTTPS to Cloudflare | Same as above |
local | Local address for outbound UDP | No external query |
local.iface:eth0 | Specific interface address | No external query |
url:https://ifconfig.me | Fetches from any HTTP endpoint | Query visible to that service |
none | Disables IPv4/IPv6 updates | — |
Default is cloudflare.trace — simple, reliable, and Cloudflare already handles
your DNS API calls.
Port Forwarding
For direct-to-home HTTP/HTTPS, configure the router to forward:
WAN:443 → 192.168.31.11:443 (id3-eniac nginx)
WAN:80 → 192.168.31.11:80 (ACME HTTP-01, if not using DNS-01)
Without port forwarding, home.alienzj.org resolves correctly but connections
are refused at the router. The DNS update still works — it just doesn’t serve
traffic.
Security Considerations
| Concern | Mitigation |
|---|---|
| Home IP exposed in DNS | Cloudflare proxy (orange cloud, proxied = true) hides origin IP |
| Router attack surface | Only forward 443; keep router firmware updated |
| DNS poisoning during IP rotation | Default 5min TTL; stale window is brief |
| Token leak | Token stored in agenix, decrypted to /run/agenix/ (tmpfs), never in nix store |
| Service compromise | DynamicUser = true — isolated, no home directory access |
Cloudflare Proxy vs DNS-only
| Mode | Setting | What clients see | Latency |
|---|---|---|---|
| Proxied (orange cloud) | proxied = true | Cloudflare edge IPs | +CF hop, hidden origin |
| DNS-only (grey cloud) | proxied = false | Your real home IP | Direct, exposed origin |
Proxied mode adds a Cloudflare hop but hides your home IP from casual observers. DNS-only gives lower latency since clients connect directly.
Latency Comparison
VPS path: Client → VPS (10ms) → Tailscale (5ms) → homelab = ~15ms extra
DDNS path: Client → home IP (0ms extra) = direct
For local access (same city/ISP), the difference is negligible. For international clients, DDNS can save 10-30ms by skipping the VPS hop.
Related Modules
| Module | Role |
|---|---|
modules/services/net/ddns.nix | DDNS updater service |
modules/services/web/acme.nix | SSL certs via Cloudflare DNS-01 |
modules/services/web/nginx.nix | Reverse proxy (used in both paths) |
modules/profiles/network/ts0.nix | Tailscale overlay |
Related Docs
- Networking & VPN — Tailscale, Headscale, MagicDNS
- Networking & Proxy — Sing-box, SSH tunnels
- Web Services — Nginx, ACME
- SSH Architecture — SSH config layers, agent