A watchdog service for Docker environments that monitors the VPN state of the host router and dynamically manages the DDNS updater service container to prevent dynamic DNS updates from registering VPN exit IPs.
When utilizing dynamic DNS services (such as ddns-updater), the updater periodically checks the host's external IP address and updates your public DNS records.
However, if a VPN is currently engaged on the network gateway (e.g., using a privacy service), the updater will see the VPN provider's exit IP instead of your true ISP public IP. If it registers this VPN IP, your DNS hostname points to a shared VPN node, breaking inbound remote access to home services.
Conversely, if the VPN is exposed (disabled/naked ISP), the updater must be deployed to keep the records updated with the correct home ISP address.
The watchdog runs in the background and resolves routing conflicts automatically:
- Streams Router Logs: It opens a persistent SSH log stream to the router, monitoring a configured remote log file for real-time state changes with zero-latency.
- Inspects VPN Status: On any state change event, it queries a configured router control script to retrieve the connection state and inspects the status of the local target container.
- Self-Heals Dynamic DNS Routing:
- VPN Engaged: If the VPN is active, it shuts down and removes the target container.
- VPN Exposed: If the VPN is down (direct ISP), it deploys/restarts the target container to update records.
Before deploying the watchdog, ensure the following gateway integration points are set up:
- Remote Log Stream: A router or gateway capable of writing VPN connection state change events to a log file.
- Passwordless SSH: The watchdog service runs as
root(when installed as a service), so the host'srootuser must have passwordless SSH key-based access to the remote router. - Local Gateway Wrapper: A local script capable of querying the gateway's VPN status and returning state strings.
All configuration is externalized in fix-ddns.cfg. If the file is missing, the script automatically generates it on its first run with sensible defaults:
TARGET_CONTAINER: The name of the DDNS updater container to monitor (defaults toddns-updater).DOCKER_ROOT: The root directory where Docker Compose stacks are located.COMPOSE_DIR: The compose directory containing the DDNS updater stack.ROUTER_CTRL: Absolute path to the local router control script wrapper.ROUTER_LOG_PATH: Path to the log file streamed on the router via SSH.EVENT_PATTERN: Log event trigger string that initiates evaluation (defaults to=== EVENT START ===).DEBOUNCE_COOLDOWN: Cooldown (in seconds) to wait after an evaluation to prevent consecutive restarts.SSH_RETRY_INTERVAL: Interval (in seconds) to wait before reconnecting if the SSH stream drops.LOG_RETENTION_EVENT_HOURS&LOG_RETENTION_INFO_HOURS: Retain event logs and info logs respectively (in hours).
Manage the service using script flags:
- Check status:
bash fix-ddns.sh --check - Install as a system service:
sudo bash fix-ddns.sh --install - Remove service:
sudo bash fix-ddns.sh --purge - Run in foreground (debug/testing):
bash fix-ddns.sh --watch
Create a directory for the script and download it using one of the following commands:
Using curl:
mkdir -p fix-ddns && cd fix-ddns && curl -sSL https://raw.githubusercontent.com/Arelius-D/fix-ddns/main/fix-ddns.sh -o fix-ddns.sh && chmod +x fix-ddns.shUsing wget:
mkdir -p fix-ddns && cd fix-ddns && wget -q https://raw.githubusercontent.com/Arelius-D/fix-ddns/main/fix-ddns.sh -O fix-ddns.sh && chmod +x fix-ddns.sh