Dynamic DNS
Chapter 7 — Dynamic DNS
Consumer ISPs like Virgin Media assign dynamic IP addresses — the same address isn't guaranteed to stick. When your IP changes, any DNS A record pointing directly at your home IP becomes stale and your server becomes unreachable. Dynamic DNS solves this automatically: a daemon on your server detects the change and updates the DNS record within minutes. This chapter covers how DDNS works, when Cloudflare Tunnel removes the requirement entirely, and two practical implementations: DuckDNS (simplest) and Cloudflare API (best for the existing setup).
The Dynamic IP Problem
Static IP addresses cost extra on consumer broadband and Virgin Media doesn't offer them on residential plans. Your IP is leased by DHCP from the ISP and renewed periodically — or reassigned whenever the router reboots, after a long power cut, or just at the ISP's discretion.
The key parameters that determine downtime are the detection interval (how often the daemon checks your IP) and the DNS TTL (how long resolvers cache the old IP). If you check every 5 minutes and TTL is 60 seconds, maximum downtime is about 6 minutes.
When Cloudflare Tunnel Removes the DDNS Requirement
If your web traffic uses Cloudflare Tunnel (Chapter 4), your home IP is not in the DNS records for your website at all. The DNS records point to Cloudflare's edge (the tunnel CNAME), not your home IP. The tunnel itself is an outbound connection — it reconnects automatically with whatever IP your router currently has.
home.osztromok.com or ssh.osztromok.com updated by DDNS — not proxied through Cloudflare (so it exposes the real IP directly for SSH). This record doesn't route web traffic and doesn't need the tunnel.
DDNS Provider Options
- Use your own domain (osztromok.com)
- Update any record you control
- Works with ddclient or a curl script
- Free — uses your existing Cloudflare account
yourname.duckdns.org subdomain.- Completely free, no credit card
- Login with GitHub/Google/etc.
- One-liner curl update command
- Subdomain is .duckdns.org (not your own domain)
- Free tier: 3 hostnames
- Must confirm free hostnames monthly
- Supports custom domains on paid plan
- Has its own official Linux client
- Free custom domain DDNS
- No monthly confirmation needed
- Supports ddclient
- Less well-known but reliable
Checking Your Public IP
DDNS clients work by comparing your current public IP against what's in the DNS record. Checking your IP from the server itself requires querying an external service — ip route only shows your local network IP, not the public one the internet sees.
Method 1 — DuckDNS (Simplest)
duckdns.org, log in with GitHub/Google, and create a subdomain — e.g. osztromok.duckdns.org. DuckDNS shows you your token on the dashboard. Copy it — you'll need it in the update URL.
ssh philip@osztromok.duckdns.org. It always resolves to your current home IP. TTL is 60 seconds, so after an IP change it's current within a minute of the next cron run.Method 2 — Cloudflare API with ddclient
ddclient is a mature DDNS daemon that supports dozens of providers including Cloudflare. It runs as a system service, detects IP changes, and updates the DNS record. This approach lets you keep a record like ssh.osztromok.com on your own domain pointing at your real home IP.
Step 1 — Create a Cloudflare API token
Go to Cloudflare dashboard → My Profile → API Tokens → Create Token. Use the Edit zone DNS template:
- Permissions: Zone → DNS → Edit
- Zone Resources: Include → Specific zone → osztromok.com
- Click Continue and Create Token. Copy the token — it's only shown once.
Step 2 — Create the DNS record to be updated
In Cloudflare DNS, add an A record for the SSH hostname:
| Type | Name | Content | Proxy | TTL |
|---|---|---|---|---|
| A | ssh | 82.2.236.221 (current IP) | Grey cloud (DNS only) | 60 seconds |
Grey cloud (DNS only) is essential here — this record needs to expose the real IP so SSH clients can connect directly. The orange cloud would hide the IP behind Cloudflare, which breaks SSH. TTL 60 seconds means the record updates are visible within a minute.
Step 3 — Install ddclient
Step 4 — Write the ddclient config
home.osztromok.com, add it on a new line after ssh.osztromok.com.
Alternative — Pure Bash Script via Cloudflare API
If you'd rather not install ddclient, the same result is achievable with a short bash script and cron. This is useful if you want full visibility into what's happening or want to add custom logic (notifications, logging).
Systemd Timer — Modern Alternative to Cron
Systemd timers are the modern replacement for cron on Debian/Ubuntu systems. They have better logging (integrated with journald), dependency support, and can be randomised to avoid thundering-herd problems on servers that all update at exactly the same second.
Alerting When Your IP Changes
Worth adding a notification so you know when an IP change happens — useful for auditing that DDNS actually worked and for spotting unexpected IP changes (which might indicate a router issue).
Troubleshooting
sudo ddclient -daemon=0 -debug -verbose -noquiet. Common causes:
— Auth failure: "login" is wrong — in ddclient.conf,
login=token is literal text (not your email), password= is the API token value.
— Wrong zone: the
zone= value must exactly match your Cloudflare zone name.
— Record not found: the hostname at the bottom of the config must match an existing DNS record in Cloudflare. Create the A record manually first, then ddclient will update it.
osztromok, not osztromok.duckdns.org). Test manually: curl "https://www.duckdns.org/update?domains=YOURDOMAIN&token=YOURTOKEN&ip="dig ssh.osztromok.com +short — if the record is grey cloud (DNS only), Cloudflare should apply the TTL you set (60 seconds is the minimum). If it's proxied/orange cloud, Cloudflare caches differently. Wait the TTL interval and dig again./usr/bin/curl not curl). Check cron's own log: grep CRON /var/log/syslog | tail -20. Also verify the script is executable (chmod +x) and that the crontab line has no typos in the timing fields.
ssh.osztromok.com uses a DNS A record that points to your real IP — this record needs DDNS to stay current. Verify DDNS is running: sudo systemctl status ddclient or check the state file: cat /var/cache/cf-ddns-last-ip and compare against curl -s https://api.ipify.org.
Quick Reference — Chapter 7
| Command | Purpose |
|---|---|
| curl -s https://api.ipify.org | Get your current public IP from the server |
| dig +short ssh.osztromok.com | Check what IP is currently in the DNS record |
| sudo ddclient -daemon=0 -debug -verbose -noquiet | Run ddclient once in foreground with full debug output — best troubleshooting tool |
| sudo journalctl -u ddclient -f | Watch ddclient daemon logs live |
| sudo journalctl -u cf-ddns.service --since today | View today's systemd timer runs for the custom script |
| sudo systemctl list-timers | Show all timers and when they last/next ran |
| crontab -l | List current user's cron jobs |
| grep CRON /var/log/syslog | tail -20 | Check whether cron ran recently (and if it logged errors) |
| Scenario | DDNS needed? | Best approach |
|---|---|---|
| Website via Cloudflare Tunnel | No | Tunnel handles it — no home IP in DNS |
| SSH into home server from outside | Yes | A record (grey cloud, TTL 60s) + ddclient updating it |
| Direct connection (no tunnel) | Yes | A record + ddclient or DuckDNS |
| Just want to know your current IP | No | curl -s https://api.ipify.org |
| File / Location | Purpose |
|---|---|
| /etc/ddclient.conf | ddclient configuration — provider, token, records to update. Chmod 600. |
| /var/cache/ddclient.cache | ddclient's record of the last IP it sent — compared on each check |
| /var/cache/cf-ddns-last-ip | State file for the custom bash script approach |
| ~/duckdns/duck.log | DuckDNS update response log (OK or KO) |
| /var/log/ip-changes.log | Custom IP change history log |