- Shell 94.9%
- Just 5.1%
| cdmon | ||
| cloudflare | ||
| duckdns | ||
| .env.example | ||
| .gitignore | ||
| justfile | ||
| README.md | ||
ddns-updater
Multi-provider Dynamic DNS updater. One folder per DNS provider, each with a self-contained update.sh. All configuration lives in a single root .env.
Providers
| Provider | Status |
|---|---|
| cdmon | ✅ Implemented |
| cloudflare | ✅ Implemented |
| duckdns | ✅ Implemented |
Setup
-
Copy
.env.exampleto.envand fill in the relevant sections:cp .env.example .env $EDITOR .env -
Test manually:
bash cdmon/update.sh bash cloudflare/update.sh bash duckdns/update.sh # or via just: just run-cdmon -
Add to crontab (hourly, with logging):
0 * * * * /home/dvitto/projects/ddns-updater/cdmon/update.sh >> /mnt/services/ddns-updater/logs/cdmon.log 2>&1
Configuration
All settings are in the root .env (see .env.example). Notification credentials are shared; provider credentials are in separate sections.
CDMON
CDMON_USER=<cdmon-ddns-user>
CDMON_PASS=<cdmon-ddns-password>
CDMON_DOMAINS=example.com,other.com
CDMON_SLEEP=15 # seconds between requests — CDMON rate-limits at ~10s
Credentials come from the CDMON panel → Dynamic DNS section. The same user/password applies to all listed domains.
API: GET https://dinamico.cdmon.org/onlineService.php?enctype=MD5&n=USER&p=MD5PASS&cip=IP — success response contains customok.
Cloudflare
CF_API_TOKEN=<api-token-with-Zone:DNS:Edit>
CF_ZONE_ID=<zone-id-from-cloudflare-dashboard>
CF_DOMAINS=example.com,sub.example.com
All domains must belong to the same zone. The script skips records already set to the current IP.
DuckDNS
DUCKDNS_TOKEN=<token-from-duckdns.org>
DUCKDNS_DOMAINS=myhost1,myhost2 # subdomain names only, no .duckdns.org
DuckDNS accepts all domains in a single API call.
Notifications
Set TELEGRAM_TOKEN + TELEGRAM_CHAT_ID and/or NTFY_URL + NTFY_TOKEN + NTFY_TOPIC in .env to receive failure alerts. Leave empty to disable.
Logs
Cron output goes to /mnt/services/ddns-updater/logs/cdmon.log, rotated via /etc/logrotate.d/backup-logs.