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
  • 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 important, 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 IP

The Script

The script executes four steps:

  1. Detect current public IPv4
  2. Read previously stored IP from local state
  3. Compare values
  4. Update deSEC only if the IP changed

It also:

  • logs actions
  • handles failures cleanly
  • avoids unnecessary API calls

Repository: Main Repo Script Folder + Readme

Installation

  1. Create directories
 sudo mkdir -p /etc/desec-ddns /var/lib/desec-ddns
  1. Store the token
sudo nano /etc/desec-ddns/token

Paste 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/token
  1. Install the script
sudo cp desec-ddns.py /usr/local/bin/
sudo chmod +x /usr/local/bin/desec-ddns.py
  1. Test manually - Optional
sudo /usr/local/bin/desec-ddns.py

Verify:

dig +short YOUR.domain.com
curl -4 -s https://api.ipify.org ; echo
  1. Schedule with cron
sudo crontab -e
0 * * * * /usr/local/bin/desec-ddns.py >> /var/log/desec-ddns.log 2>&1

Runs once per hour. More than enough unless your ISP is feeling particularly chaotic.

Key Implementation Details

The script explicitly sends: myipv6=preserve This 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

Previous Post