Implementing Dynamic DNS (DDNS) with a Bash Script and DNSPod API
This article details a Bash script client for Dynamic DNS (DDNS) that automatically updates domain records by calling the DNSPod API. It supports Token-based authentication (the official, more secure method) and fetches the public IP address from the local network interface, which is useful when an ISP's caching server provides an inaccurate external IP.
Key Features
- Token Authentication: Uses DNSPod's recommended Token method for improved security.
- Local IP Retrieval: Obtains the public IP by parsing local interface data, avoiding errors from ISP cache servers.
- Multi-Domain Support: Configure multiple domains and subdomains for batch management.
- Logging: Outputs runtime logs to
/var/log/dnspodsh.logfor troubleshooting. - Error Handling: Validates API response status and includes logic to prevent account lockout from frequent errors.
Requirements and Notes
- Shell Environment: The script requires Bash. Some embedded systems (e.g., OpenWrt) use Ash by default; install Bash manually if needed.
- API Rate Limits: DNSPod API has strict limits. More than 30 failed login attempts within 5 minutes can temporarily disable the account. The script includes status checks; avoid excessive calls.
- Configuration: Before use, configure the
login_token(or legacy email/password), the domain list (domainList), and the check interval (delay).
Core Script Code
Below is the core script, updated with current DNSPod API best practices:
#!/bin/bash
# Configuration Section
login_token="YOUR_ID,YOUR_TOKEN" # Format: ID,Token (from DNSPod console)
format="json"
lang="cn"
userAgent="dnspodsh/0.5"
apiUrl="https://dnsapi.cn/"
# Domain list example: each element is 'domain subdomain1 subdomain2'
domainList[0]="example.com www subdomain"
# domainList[1]="another.com @ *" # @ for root, * for wildcard (needs escaping)
delay=300 # Check interval in seconds
logDir="/var/log"
logFile="$logDir/dnspodsh.log"
commonPost="login_token=$login_token&format=$format&lang=$lang"
# IP validation function
checkip() {
# IPv4 check
if [[ "$1" =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]]; then
return 0
# Simplified IPv6 check
elif [[ "$1" =~ ^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$ ]]; then
return 0
fi
return 1
}
# API call function
getUrl() {
curl -s -A "$userAgent" -d "$commonPost&$2" "$apiUrl$1"
}
# Logging function
writeLog() {
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
if [ -w "$logDir" ]; then
echo "[$timestamp] $*" >> "$logFile"
fi
echo "[$timestamp] $*"
}
# Get public IP from local interface
getLocalIP() {
ip -4 -o addr show scope global | awk 'NR==1 {print $4}' | cut -d'/' -f1
}
# Main update function
go() {
newip=$(getLocalIP)
if ! checkip "$newip"; then
writeLog "Could not get a valid IP address. Skipping check."
return 1
fi
writeLog "Current detected IP: $newip"
# Logic for getChangedRecords and setRecords would go here
}
# Daemon loop
while true; do
go
sleep $delay
done
Deployment and Usage
- Get a Token: Log into the DNSPod Console, create an API Token (format:
ID,Token). - Configure the Script: Save the code as
dnspodshand edit thelogin_tokenanddomainList. - Install Bash (if needed): On systems like OpenWrt, run
opkg update && opkg install bash. - Set Executable Permission: Run
chmod +x dnspodsh. - Auto-start on Boot: Add this line to
/etc/rc.local(beforeexit 0):/path/to/dnspodsh >/dev/null 2>&1 &
Troubleshooting
- Script error "not found": Likely a shell incompatibility. Ensure Bash is installed and the script uses
#!/bin/bash. - Records not updating: Verify the Token has read/write permissions for the domain and the domain configuration format is correct.
- No log output: Confirm the
/var/logdirectory is writable, or modify thelogDirvariable.
Note: The original script was published in 2015; some logic may be outdated. Consider using DNSPod's official DDNS tools or actively maintained third-party clients (e.g., ddns-go) first. If using this script, adjust parameters according to the latest API documentation.