Skip to content

Instantly share code, notes, and snippets.

@mbierman
Last active April 3, 2026 20:45
Show Gist options
  • Select an option

  • Save mbierman/d4c6aac8e741fb0cbb289ce1899df59a to your computer and use it in GitHub Desktop.

Select an option

Save mbierman/d4c6aac8e741fb0cbb289ce1899df59a to your computer and use it in GitHub Desktop.
DDNS update for Cloudflare with dual WAN support
#!/usr/bin/env bash
# Enter your cloudflare credentials
zoneID=""
ID=""
APIToken=""
# the DNS record
name="domain"
# your WAN port eg. eth0
interface=eth2
retention=2020
DIRECTORY=/data/logs
log="$DIRECTORY/updateIP.log"
echo $log
logMe () {
tail -n "$retention" "$log" > "$log.tmp"
mv "$log.tmp" "$log"
}
# 1. Get Current Local IPs
IPINFO=$(curl -s https://ipinfo.io/)
currentIP=$(echo "$IPINFO" | jq -r ".ip")
currentIPv6=$(curl -s --interface "$interface" https://6.icanhazip.com)
# 2. Update NextDNS
curl -s --interface "$interface" "https://link-ip.nextdns.io/42eadb/7dc5b7251af22fd2" > /dev/null
# --- IPv4 Logic (Type A) ---
# Fetch existing record info
CF_V4_JSON=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records?type=A&name=$name" \
-H "Authorization: Bearer $APIToken" -H "Content-Type: application/json")
ID_V4=$(echo "$CF_V4_JSON" | jq -r '.result[0].id // empty')
cloudflaresays_v4=$(echo "$CF_V4_JSON" | jq -r '.result[0].content // empty')
if [ "$currentIP" = "$cloudflaresays_v4" ]; then
echo -e "======\n$(date) IPv4: No change required ($currentIP)" | tee -a "$log"
else
comment="updated: $(date)"
echo -e "======\n$(date) IPv4: Updating to $currentIP" | tee -a "$log"
curl --interface "$interface" -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records/$ID_V4" \
-H "Authorization: Bearer $APIToken" -H "Content-Type: application/json" \
--data "{\"type\":\"A\",\"name\":\"$name\",\"content\":\"$currentIP\",\"ttl\":600,\"proxied\":false,\"comment\":\"$comment\"}"
fi
# --- IPv6 Logic (Type AAAA) ---
if [ -z "$currentIPv6" ]; then
echo -e "======\n$(date) IPv6: Error - No local IPv6 detected." | tee -a "$log"
else
# Fetch existing record info
CF_V6_JSON=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records?type=AAAA&name=$name" \
-H "Authorization: Bearer $APIToken" -H "Content-Type: application/json")
ID_V6=$(echo "$CF_V6_JSON" | jq -r '.result[0].id // empty')
cloudflaresays_v6=$(echo "$CF_V6_JSON" | jq -r '.result[0].content // empty')
comment="updated: $(date)"
if [ "$currentIPv6" = "$cloudflaresays_v6" ]; then
echo -e "======\n$(date) IPv6: No change required ($currentIPv6)" | tee -a "$log"
elif [ -z "$ID_V6" ]; then
# Create record if ID is missing
echo -e "======\n$(date) IPv6: Record missing. Creating... ($currentIPv6)" | tee -a "$log"
curl --interface "$interface" -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records" \
-H "Authorization: Bearer $APIToken" -H "Content-Type: application/json" \
--data "{\"type\":\"AAAA\",\"name\":\"$name\",\"content\":\"$currentIPv6\",\"ttl\":600,\"proxied\":false,\"comment\":\"$comment\"}"
else
# Update existing record
echo -e "======\n$(date) IPv6: Updating to $currentIPv6" | tee -a "$log"
curl --interface "$interface" -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records/$ID_V6" \
-H "Authorization: Bearer $APIToken" -H "Content-Type: application/json" \
--data "{\"type\":\"AAAA\",\"name\":\"$name\",\"content\":\"$currentIPv6\",\"ttl\":600,\"proxied\":false,\"comment\":\"$comment\"}"
fi
fi
logMe
@mbierman
Copy link
Copy Markdown
Author

Run through cron.
To create scheduled jobs, you may add your own cronjob by putting cronjob expression in this file. It will be incorporated to the system crontab when Firewalla service restarts.

/home/pi/.firewalla/config/user_crontab
Example:

pi@firewalla:~ (Firewalla) $ cat ~/.firewalla/config/user_crontab

          • /bin/bash -c "date" &> /tmp/date.log
            You can verify if it's incorporated by running "crontab -l"

See also https://github.com/ccpk1/Firewalla-Gold-Tools/blob/main/cloudflare/cloudflare_ddns_update.sh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment