← All articles
gray Synology machine

Dynamic DNS for Homelab Remote Access

Networking 2026-03-04 · 3 min read ddns dynamic dns remote access homelab cloudflare duckdns networking self-hosted
By HomeLab Starter Editorial TeamHome lab enthusiasts covering hardware setup, networking, and self-hosted services for home and small office environments.

Most home internet connections have dynamic IP addresses — your ISP assigns a new address periodically. Dynamic DNS (DDNS) solves the problem of accessing your homelab remotely by automatically updating a DNS record whenever your IP changes. Your homelab is reachable at home.yourdomain.com even as the underlying IP changes.

Photo by Claudio Schwarz on Unsplash

How DDNS Works

  1. You register a hostname (e.g., home.yourdomain.com or myhomelab.duckdns.org)
  2. A client on your home network periodically checks your public IP
  3. When the IP changes, the client updates the DNS record
  4. Traffic to your hostname routes to your current IP

The check interval determines how long your hostname might point to the old IP after a change. Every 5 minutes is typical for most home setups.

Option 1: Cloudflare DDNS (Recommended for Custom Domains)

If you already use Cloudflare for DNS:

Script to update Cloudflare DNS record:

#!/bin/bash
# /usr/local/bin/cloudflare-ddns.sh

ZONE_ID="your-cloudflare-zone-id"
RECORD_NAME="home.yourdomain.com"
API_TOKEN="your-cloudflare-api-token"

# Get current public IP
CURRENT_IP=$(curl -s https://api.ipify.org)

# Get the current DNS record
RECORD=$(curl -s \
  "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?type=A&name=${RECORD_NAME}" \
  -H "Authorization: Bearer ${API_TOKEN}")

RECORD_ID=$(echo "$RECORD" | python3 -c "import sys,json; data=json.load(sys.stdin); print(data['result'][0]['id'])" 2>/dev/null)
CURRENT_RECORD_IP=$(echo "$RECORD" | python3 -c "import sys,json; data=json.load(sys.stdin); print(data['result'][0]['content'])" 2>/dev/null)

# Update if IP has changed
if [ "$CURRENT_IP" != "$CURRENT_RECORD_IP" ]; then
  echo "$(date): IP changed from $CURRENT_RECORD_IP to $CURRENT_IP. Updating..."
  curl -s -X PATCH \
    "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
    -H "Authorization: Bearer ${API_TOKEN}" \
    -H "Content-Type: application/json" \
    --data "{\"type\":\"A\",\"name\":\"${RECORD_NAME}\",\"content\":\"${CURRENT_IP}\",\"ttl\":120,\"proxied\":false}"
fi

Run every 5 minutes via cron:

*/5 * * * * /usr/local/bin/cloudflare-ddns.sh >> /var/log/ddns.log 2>&1

Docker alternative (easier to manage):

services:
  cloudflare-ddns:
    image: oznu/cloudflare-ddns:latest
    restart: always
    environment:
      API_KEY: your-cloudflare-api-token
      ZONE: yourdomain.com
      SUBDOMAIN: home
      PROXIED: "false"

Create a Cloudflare API token with "Zone:DNS:Edit" permission scoped to your zone.

Option 2: DuckDNS (Free, No Domain Required)

DuckDNS offers free *.duckdns.org subdomains with simple API updates:

  1. Go to duckdns.org, log in with Google/GitHub
  2. Create a subdomain: yourhomelab.duckdns.org
  3. Copy your token

Update script:

#!/bin/bash
# /usr/local/bin/duckdns-ddns.sh

DOMAIN="yourhomelab"
TOKEN="your-duckdns-token"

curl -s "https://www.duckdns.org/update?domains=${DOMAIN}&token=${TOKEN}&ip=" >> /var/log/duckdns.log 2>&1

DuckDNS auto-detects your IP when the ip= parameter is empty.

Docker:

services:
  duckdns:
    image: lscr.io/linuxserver/duckdns:latest
    environment:
      PUID: 1000
      PGID: 1000
      TZ: America/Los_Angeles
      SUBDOMAINS: yourhomelab
      TOKEN: your-duckdns-token
    restart: unless-stopped

Option 3: ddclient (Universal DDNS Client)

ddclient supports dozens of DDNS providers (DynDNS, No-IP, DuckDNS, Cloudflare, Namecheap, and many more):

sudo apt install ddclient

/etc/ddclient.conf:

# For DuckDNS
protocol=duckdns
login=yourhomelab
password=your-duckdns-token
yourhomelab.duckdns.org

# For Cloudflare
protocol=cloudflare
zone=yourdomain.com
[email protected]
password=your-api-token
ttl=1
home.yourdomain.com
sudo systemctl enable ddclient
sudo systemctl start ddclient

Getting Your Zone ID and API Token (Cloudflare)

Zone ID: Cloudflare Dashboard → select your domain → Overview → right sidebar → Zone ID

API Token: Cloudflare Dashboard → My Profile → API Tokens → Create Token

Setting TTL Low

When using DDNS, set a low TTL (60-300 seconds) on the DNS record. This determines how long cached records remain valid — lower TTL means faster propagation of IP changes, but more DNS queries.

Cloudflare's minimum TTL for free accounts is 120 seconds. That's acceptable for most homelab use.

Handling IPv6

If your ISP provides IPv6 (increasingly common), you can add an AAAA record alongside the A record. The Cloudflare DDNS script can be adapted:

# Get IPv6 address
CURRENT_IPV6=$(curl -s https://api6.ipify.org)
# Then update AAAA record the same way

Combining with Reverse Proxy

The typical homelab setup:

  1. DDNS points home.yourdomain.com to your public IP
  2. Port 443 (HTTPS) forwarded to your reverse proxy (Nginx Proxy Manager, Caddy, Traefik)
  3. Reverse proxy routes to internal services based on subdomain

This lets you access paperless.home.yourdomain.com, gitea.home.yourdomain.com, etc. all through one port with HTTPS.

Security Note

Exposing your homelab to the internet via DDNS requires careful security:

DDNS with a reverse proxy is a good setup for services you actively use from outside. For highly sensitive services, Tailscale (VPN) is a more secure option that avoids public exposure entirely.

Get free weekly tips in your inbox. Subscribe to HomeLab Starter