Networking Ddns

Dynamic DNS (DDNS) — Direct-to-Home Networking

How home.alienzj.org stays pointed at a dynamic residential IP using Cloudflare DDNS, and why you might (or might not) want to bypass the VPS.


Concepts

IP Types

TypeExampleWho gets it
Static publicVPS IPDatacenter providers, business ISPs
Dynamic public198.51.100.1203.0.113.1Residential ISPs, rotates periodically
CGNAT100.64.x.x (RFC 6598)Mobile ISPs, some residential ISPs
Private192.168.x.x, 10.x.x.xBehind 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
ProsCons
Static IP, no DDNS neededExtra latency (VPS hop)
VPS filters malicious trafficVPS bandwidth costs
TLS terminates at VPSVPS 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
ProsCons
Zero VPS latencyDynamic IP — stale DNS during rotation window
No VPS bandwidth costsExposes home IP to DNS
Direct path, fewer hopsNeeds 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

  1. cloudflare-ddns service starts at boot (after network-online.target)
  2. Reads CLOUDFLARE_API_TOKEN from agenix secret (/run/agenix/cloudflare-api-token)
  3. Detects public IP via cloudflare.trace (Cloudflare’s /cdn-cgi/trace endpoint)
  4. Updates the home.alienzj.org A record via Cloudflare API
  5. 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

  1. Cloudflare API token with Zone.DNS.Edit permission (already exists for ACME)
  2. DNS record home.alienzj.org must exist in Cloudflare (even as a dummy)
  3. 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:

ProviderHow it worksPrivacy
cloudflare.traceParses /cdn-cgi/traceCloudflare already sees your DNS
cloudflare.dohDNS-over-HTTPS to CloudflareSame as above
localLocal address for outbound UDPNo external query
local.iface:eth0Specific interface addressNo external query
url:https://ifconfig.meFetches from any HTTP endpointQuery visible to that service
noneDisables 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

ConcernMitigation
Home IP exposed in DNSCloudflare proxy (orange cloud, proxied = true) hides origin IP
Router attack surfaceOnly forward 443; keep router firmware updated
DNS poisoning during IP rotationDefault 5min TTL; stale window is brief
Token leakToken stored in agenix, decrypted to /run/agenix/ (tmpfs), never in nix store
Service compromiseDynamicUser = true — isolated, no home directory access

Cloudflare Proxy vs DNS-only

ModeSettingWhat clients seeLatency
Proxied (orange cloud)proxied = trueCloudflare edge IPs+CF hop, hidden origin
DNS-only (grey cloud)proxied = falseYour real home IPDirect, 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.


ModuleRole
modules/services/net/ddns.nixDDNS updater service
modules/services/web/acme.nixSSL certs via Cloudflare DNS-01
modules/services/web/nginx.nixReverse proxy (used in both paths)
modules/profiles/network/ts0.nixTailscale overlay