SKYHOUSE.dev Journal

Maintaining the Cloud Fortress

Dynadot DDNS Pipeline (Discovery)

Why: Documenting an active (but currently broken) service that pre-dates the journal — captured during the 2026-05 audit so the cheat sheet has a citation target and the known bug has a permanent record.

This server sits behind a residential ISP connection with a dynamic public IP. To keep DNS records pointing at the right address, a custom DDNS pipeline runs every five minutes from the user crontab. The system has two moving parts: a Bash poller that detects IP changes and a Python script that talks to the Dynadot API. A separate ddns-go container also exists from an earlier implementation; the relationship between the two is a known open question (see below).

1. ip-watch.sh and dynadot-update.py

/home/plex/bin/ip-watch.sh runs on a */5 * * * * cron schedule (user crontab, plex). It compares the current public IP against a state file at /home/plex/.cache/ip-watch.last. When a change is detected it calls /home/plex/bin/dynadot-update.py, passing the domain list and new IP. dynadot-update.py authenticates to the Dynadot API and updates the A record(s). The domain list and Telegram credentials consumed by both scripts are stored in /home/plex/.messaging-keys (mode 0600, plex:plex) — refer to that file for the actual values; they are not reproduced here.

Logs go to /home/plex/.cache/ip-watch.log. A Telegram notification is sent on a successful update via /usr/local/bin/telegram_notify.sh.

As of the audit, .messaging-keys lists only skyhouse.dev in DOMAINS. The runbook describes the expected configuration as skyhouse.dev,room101.com; room101.com is currently unmanaged and its A record is stale.

2. ddns-go container

A jeessy/ddns-go container is running and exposes a management UI on port 9876 (LAN only — no external port forward). Per the February 2026 monitoring-setup doc, ddns-go was the original DDNS path. Today, ip-watch.sh is the active authority for Dynadot updates. The audit did not determine whether ddns-go is still configured to manage any records or is effectively idle. Resolving which system is authoritative is a pending Phase B decision — until then, both coexist.

3. Known issue: set -e / ((x++)) bug stalls state-file writes

The audit found a bug in /home/plex/bin/ip-watch.sh that causes the script to silently exit before writing the state file. The line ((success_count++)) uses Bash's arithmetic command form, which exits with code 1 when the expression evaluates to 0 (i.e., on the very first iteration, before any increment). Because the script opens with set -euo pipefail, this non-zero exit kills the process immediately after calling dynadot-update.py.

Operational impact: DNS for skyhouse.dev happens to be correct because the first-ever run updated it successfully before the bug fires. However, /home/plex/.cache/ip-watch.last is permanently stuck at the pre-update IP (174.21.177.8), so any future IP change will not auto-update — the script will re-detect the change every five minutes but never advance past the python call. The fix (success_count=$((success_count + 1))) is straightforward but has not yet been applied; a later session will patch it.

Until the bug is fixed, DDNS auto-update is effectively broken for any future IP change — manual intervention would be required.

← Back to Admin Hub