KingDezigns — DDNS Updater Summary

📅 May 19, 2025 🖥 NAS16 (Raspberry Pi) ⚙️ OpenMediaVault ☁️ DreamHost DNS API 🌐 20 A Records · 4 Domains
What was built

A self-contained Bash script that keeps all KingDezigns DreamHost A records synchronized with the current home WAN IP. It runs every 15 minutes via OMV Scheduled Tasks. It only calls the DreamHost API when the IP actually changes (or during a nightly sanity check), so there is zero risk of being banned for spamming.

20 A records managed 4 domains Max 2 API calls per event HTML email notifications Every 15 min via cron Daily sanity check 00:00–00:14
Logic flow map
⏱ Script runs (every 15 min via OMV cron)
🌐 Get current WAN IP from api.ipify.org / ifconfig.me / icanhazip.com
📄 Read cached previous IP from /var/log/ddns/last_wan_ip.txt
🕛 Is the time between 00:00 and 00:14? (daily sanity-check window)
IP changed OR daily window
▶ Query DreamHost — check each A record
📡 Call dns-remove_record + dns-add_record for each mismatched record
⏳ Wait 3–5 minutes (random)
🔍 Re-query DreamHost — verify each record
Still wrong
🔁 Attempt 2 of 2 — update remaining records
⏳ Wait 3–5 min · Re-verify
Still failing
✗ STOP (max attempts reached)
📧 Email: partial failure + manual fix instructions + WAN IP
Now correct
✓ Mark success
📧 Email: all records updated
All correct
✓ All records verified
📧 Email: success — all records updated
💾 Write new WAN IP to cache file
IP unchanged + not daily window
💾 Overwrite cache with same IP (refreshes timestamp)
🔇 Exit silently — no API calls, no email
⚠ DreamHost API — how updates work

DreamHost has no "update" DNS command. To change an A record you must remove the old value then add the new one. The script always fetches the live DreamHost value immediately before each remove/add pair so it never tries to remove a value that isn't there.

Domains & records managed
Record (DreamHost "record" field)Zone / DomainType
albranch.orgalbranch.orgA (root / @)
www.albranch.orgalbranch.orgA
indigorhayne.comindigorhayne.comA (root / @)
www.indigorhayne.comindigorhayne.comA
rufusking.comrufusking.comA (root / @)
www.rufusking.comrufusking.comA
kingdezigns.comkingdezigns.comA (root / @)
www.kingdezigns.comkingdezigns.comA
*.web.kingdezigns.comkingdezigns.comA (wildcard)
cloud.kingdezigns.comkingdezigns.comA
files08.kingdezigns.comkingdezigns.comA
files16.kingdezigns.comkingdezigns.comA
has.kingdezigns.comkingdezigns.comA
nginx.kingdezigns.comkingdezigns.comA
omv08.kingdezigns.comkingdezigns.comA
omv16.kingdezigns.comkingdezigns.comA
pihole.kingdezigns.comkingdezigns.comA
plex.kingdezigns.comkingdezigns.comA
vault.kingdezigns.comkingdezigns.comA
webmin.kingdezigns.comkingdezigns.comA
Deployment — step by step
1 Install dependencies
sudo apt-get update
sudo apt-get install -y curl python3 mailutils

curl fetches WAN IP and calls the DreamHost API. python3 parses the JSON responses. mailutils provides the mail command for sending HTML notifications.

2 Copy the script to its permanent location
sudo cp ddns_update.sh /usr/local/sbin/ddns_update.sh

This puts it in a root-only system bin directory alongside other admin utilities.

3 Set ownership and permissions
sudo chown root:root /usr/local/sbin/ddns_update.sh
sudo chmod 700 /usr/local/sbin/ddns_update.sh

The script reads/writes to /var/log/ddns/ and calls sendmail — it must run as root. Mode 700 prevents other users from reading the embedded API key.

4 Create the log directory
sudo mkdir -p /var/log/ddns
sudo chmod 700 /var/log/ddns

The script will create this automatically on first run, but creating it manually with 700 ensures no other user can read the cached IP or logs before then.

5 Test a manual run
sudo /usr/local/sbin/ddns_update.sh

Run once by hand to verify API connectivity, confirm all 20 records are found, and check that the email arrives at rufus.king@kingdezigns.com. Watch the output — it prints everything it does.

On the very first run there is no cached IP, so it will treat every record as new and perform a full update cycle.

6 Verify the log file after test run
sudo cat /var/log/ddns/ddns_run.log

You should see the WAN IP detected, DreamHost responses for each record, and "DDNS run complete" at the bottom.

7 Add OMV Scheduled Task
OMV → System → Scheduled Tasks → + (Add)

  Enabled:   ✓
  Minute:    */15
  Hour:      *
  Day:       *
  Month:     *
  Weekday:   *
  User:      root
  Command:   /usr/local/sbin/ddns_update.sh

This fires the script every 15 minutes around the clock. OMV uses standard cron under the hood — you can verify with sudo crontab -l after saving.

Script is now live and fully automated.

Script & files created
Script location
/usr/local/sbin/ddns_update.sh
IP cache (previous WAN IP)
/var/log/ddns/last_wan_ip.txt
Run log (last 5000 lines, auto-trimmed)
/var/log/ddns/ddns_run.log
Notification email
rufus.king@kingdezigns.com
Manual run command
sudo /usr/local/sbin/ddns_update.sh
Force a fresh update (clears cache)
sudo rm /var/log/ddns/last_wan_ip.txt && sudo /usr/local/sbin/ddns_update.sh
Watch the live log
sudo tail -f /var/log/ddns/ddns_run.log
Email notification behavior
ScenarioEmail sent?Subject line
IP unchanged, not daily window No Silent exit — no email
IP changed, all records updated successfully Yes [DDNS] ✅ All Records Updated — <new IP>
IP changed, some records failed after 2 attempts Yes — action needed [DDNS] ⚠️ PARTIAL FAILURE — Manual action needed — <new IP>
Daily sanity check (00:00–00:14), all records match Yes [DDNS] ✅ All Records Updated — <IP>
Daily sanity check, mismatch found & fixed Yes [DDNS] ✅ All Records Updated — <new IP>
WAN IP discovery fails (all 3 sources down) No Script aborts — will retry next 15-min run

All emails are sent as HTML using the KingDezigns branded template — same typography and color system as this document. They include a record-by-record results table and, on failure, a direct link to the DreamHost DNS panel with the correct IP pre-noted for manual entry.

Important notes & gotchas
ItemDetail
API key is embedded in the script The script is chmod 700 / root only. Do not copy or share the file without scrubbing the key first.
DreamHost has no "update" API command Every update is a remove + add pair. The script always reads the live DreamHost value first so it never removes the wrong IP.
Wildcard record *.web.kingdezigns.com DreamHost supports wildcard A records via API. The script passes the literal string *.web.kingdezigns.com to dns-list_records and it will match correctly.
Mail must be configured on NAS16 Run sudo apt-get install mailutils and ensure your MTA (Postfix or ssmtp) is configured to relay outbound email. Test with: echo "test" | mail -s "test" rufus.king@kingdezigns.com
First run behavior On the very first run there is no cached IP. The script treats every record as needing an update and performs a full cycle. This is expected and correct.
Daily window fires once per day The 00:00–00:14 window aligns with the every-15-min schedule — only one run per day falls in that window. No extra logic needed.
Log rotation The run log auto-trims itself to 5000 lines on every execution. No logrotate config needed.