This document describes how inbound HTTPS traffic reaches home network services from the public internet, and how DNS is kept current despite a dynamic WAN IP.


The problem

The home WAN IP is assigned dynamically by the ISP and changes periodically. Public DNS records that point to the home need to be updated whenever this happens. At the same time, services exposed to the internet must be reachable over HTTPS with a valid certificate.


Architecture

Internet
    │
    ▼
Cloudflare DNS
  www.cornillaud.com  A  →  <WAN IP>   ← kept current by DDNS
  n8n.cornillaud.com  CNAME  →  www    ← follows www automatically
  home.cornillaud.com CNAME  →  www    ← follows www automatically
    │
    ▼
UDM Pro SE  (port forwards: 80, 443  →  10.20.0.7)
    │
    ▼
Caddy  (Docker, 10.20.0.7)
  TLS termination — Let's Encrypt certificate, auto-renewed
  Routes by hostname to internal services via Docker "proxy" network
    │
    ├──  www.cornillaud.com   →  n8n:5678
    ├──  n8n.cornillaud.com   →  n8n:5678
    └──  home.cornillaud.com  →  homeassistant:8123

DDNS — keeping DNS current

The UDM Pro SE has a built-in DDNS client (powered by inadyn) configured to update the www A record in Cloudflare whenever the WAN IP changes.

Configuration location: UniFi Network → Settings → Internet → WAN → Dynamic DNS

FieldValue
ServiceCloudflare
Hostnamewww
Domaincornillaud.com
UsernameCloudflare account email
PasswordCloudflare API token scoped to Zone → DNS → Edit for cornillaud.com

The API token is stored in 1Password (Lab vault, Cloudflare item).

Only the www A record is maintained by DDNS. All other public subdomains are CNAME records pointing to www.cornillaud.com, so they inherit the updated IP automatically without any additional DDNS configuration.


TLS certificates — Caddy and Let’s Encrypt

Caddy obtains TLS certificates automatically from Let’s Encrypt using the HTTP-01 challenge:

  1. On first HTTPS request for a hostname, Caddy contacts Let’s Encrypt
  2. Let’s Encrypt sends a challenge token that Caddy serves on port 80
  3. Let’s Encrypt verifies the response, confirming domain ownership
  4. Caddy receives the certificate and stores it in the caddy_data Docker volume
  5. Caddy renews certificates automatically before they expire (90-day validity)

No manual certificate management is required. The caddy_data volume must persist across container restarts.


Port forwards (UDM Pro SE)

Two port forwarding rules direct all inbound web traffic to Caddy:

Rule nameProtocolWAN portLAN destination
caddy-httpTCP8010.20.0.7:80
caddy-httpsTCP44310.20.0.7:443

Port 80 is required for the Let’s Encrypt HTTP-01 challenge and for HTTP→HTTPS redirects (Caddy handles these automatically).


Adding a new subdomain

  1. DNS — add a CNAME record in Cloudflare for the new subdomain pointing to www.cornillaud.com. TTL: 1 minute.
  2. Caddyfile — add a block in lab/services/caddy/Caddyfile:
    newsubdomain.cornillaud.com {
        reverse_proxy <service-container>:<port>
    }
    
  3. Docker network — ensure the target service is on the proxy Docker network (add it to that service’s compose file if not already present)
  4. Redeploy Caddy:
    ./provision-service.sh --env prod --host <host> --service caddy

Caddy fetches the certificate for the new hostname automatically on first request.