Introduction
Dynamic DNS is one of those things that sounds trivial until it breaks your entire remote access setup.
Because #lazy, I was relying on the router’s built-in DDNS (noip.com), which worked… until it didn’t. The DNS record stopped reflecting the actual WAN IP, and suddenly my WireGuard tunnels across sites became unreliable, that and the fact that noip.com services are "freeware" and there is a nuance about monthly validation.
Instead of debugging opaque router behavior, I decided to take control of the problem and build a simple, deterministic solution using deSEC.io and a small Python script.
What it does
Updates the public IP of YOUR.domain.com <example>
- Uses deSEC.io as the DNS provider
- Runs independently of the router
- Only updates DNS when the IP actually changes
- Is simple, transparent, and easy to debug, including logging
Why deSEC.io
deSEC is a DNS hosting service with:
- Full API access
- Token-based authentication
- Native DynDNS endpoint
- No dependency on proprietary router integrations
- German based and friendly with Privacy and Community
Most importantly, it behaves predictably. Which already puts it ahead of half the consumer networking stack.
Architecture
The solution is intentionally simple:
[ *nix Based LXC/VM ]
│
├── Python script (hourly cron)
│ ├── Get public IP
│ ├── Compare with last known IP
│ └── Update deSEC if changed
│
└── deSEC DNS
└── YOUR.domain.com → WAN IPRemote systems resolve the hostname and connect to hosted services using a DNS name that actually reflects reality.
The Script
The script executes four steps:
- Detect current public IPv4
- Read previously stored IP from local state
- Compare values
- Update deSEC only if the IP changed
It also:
- logs actions
- handles failures cleanly
- avoids unnecessary API calls
Repository:
Installation
1. Create directories
sudo mkdir -p /etc/desec-ddns /var/lib/desec-ddns2. Store the token
sudo nano /etc/desec-ddns/tokenPaste your deSEC token (no quotes). [Create a deSEC Token]
Secure it:
sudo chmod 600 /etc/desec-ddns/token
sudo chown root:root /etc/desec-ddns/token3. Install the script
sudo cp desec-ddns.py /usr/local/bin/
sudo chmod +x /usr/local/bin/desec-ddns.py4. Test manually - Optional
sudo /usr/local/bin/desec-ddns.pyVerify:
dig +short YOUR.domain.com
curl -4 -s https://api.ipify.org ; echo5. Schedule with cron
sudo crontab -e0 * * * * /usr/local/bin/desec-ddns.py >> /var/log/desec-ddns.log 2>&1Runs once per hour. More than enough unless your ISP is feeling particularly chaotic.
Key Implementation Details
The script explicitly sends:
myipv6=preserveThis makes sure:
- IPv6 records are not overwritten
- Only IPv4 is updated
Without this, you risk unintentionally breaking dual-stack setups.
Why Not Use the Router
- You don’t control how often it updates
- You don’t control error handling
- You don’t get logs
- When it fails, you guess
This approach gives you:
- full control
- visibility
- reproducibility
- portability
Lessons Learned
- “Built-in” features in networking gear are often black boxes
- DNS consistency is critical for VPN / Services reliability
- A 100-ish lines script can replace an unreliable vendor feature
- Owning the logic beats trusting magic
To-Do
- Monitoring/alerting when DNS drifts from actual IP
- Extend script to support IPv6 updates if needed
- Integrate with other homelab services that depend on public reachability
012. Dynamic DNS Python Script -> deSEC.io DNS Service
In this guide, we create, deploy an hourly cron job executing a Python script to update IPV4 to a deSEC.io hosted domain.