This guide shows how to create a simple Bash script that automatically updates a Cloudflare DNS A record when your public IP address changes.
This is ideal for:
- Home lab environments
- Self-hosted services
- Proxmox / VPN endpoints
- ISP connections with dynamic IP addresses
Instead of using a third-party Dynamic DNS provider, this script updates Cloudflare directly via its API.
How It Works
The script:
- Fetches your current public IP address
- Queries Cloudflare for the existing DNS record
- Compares the two
- Updates the DNS record only if the IP has changed
- Logs all activity with timestamps
This prevents unnecessary API calls and keeps logs clean.
Requirements
Install required tools:
apt install -y curl jq
You will also need:
- A Cloudflare account
- Your Zone ID
- Your DNS Record ID
- An API key or token
The Script
Create the file:
nano /root/cloudflare-ddns.sh
Paste the following:
#!/bin/bash# Variables
ZONE_ID="YOUR_ZONE_ID"
RECORD_ID="YOUR_RECORD_ID"
AUTH_EMAIL="your@email.com"
AUTH_KEY="YOUR_GLOBAL_API_KEY"
DOMAIN="yourdomain.com"
LOG_FILE="/root/ddns.log"# Fetch the current public IP
CURRENT_IP=$(curl -s https://api.ipify.org)# Get the existing IP from Cloudflare
EXISTING_IP=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: $AUTH_EMAIL" \
-H "X-Auth-Key: $AUTH_KEY" | jq -r '.result.content')# Timestamp
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")# Compare and update if needed
if [ "$CURRENT_IP" != "$EXISTING_IP" ]; then
echo "$TIMESTAMP - IP changed from $EXISTING_IP to $CURRENT_IP. Updating record..." | tee -a "$LOG_FILE" RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: $AUTH_EMAIL" \
-H "X-Auth-Key: $AUTH_KEY" \
--data '{"type":"A","name":"'"$DOMAIN"'","content":"'"$CURRENT_IP"'","ttl":1,"proxied":false}') if echo "$RESPONSE" | grep -q '"success":true'; then
echo "$TIMESTAMP - DNS record updated successfully." | tee -a "$LOG_FILE"
else
echo "$TIMESTAMP - Update failed. Response: $RESPONSE" | tee -a "$LOG_FILE"
fi
else
echo "$TIMESTAMP - IP unchanged ($CURRENT_IP). No update required." | tee -a "$LOG_FILE"
fi
Save and make executable:
chmod +x /root/cloudflare-ddns.sh
Finding Your Cloudflare IDs
Zone ID
Cloudflare Dashboard → Domain → Overview → Right Sidebar → Zone ID
Record ID
Run:
curl -X GET "https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/dns_records" \
-H "X-Auth-Email: your@email.com" \
-H "X-Auth-Key: YOUR_GLOBAL_API_KEY" \
-H "Content-Type: application/json"
Find the record for your domain and copy the "id" value.
Run the Script Manually
/root/cloudflare-ddns.sh
Check the log:
cat /root/ddns.log
Automate with Cron
Edit root crontab:
crontab -e
Run every 5 minutes:
*/5 * * * * /root/cloudflare-ddns.sh
Security Recommendation (Important)
Instead of using your Global API Key, it is strongly recommended to create a Cloudflare API Token with limited permissions:
Permissions required:
- Zone → DNS → Edit
- Zone → Zone → Read
This reduces risk if the script is ever exposed.
Optional Improvements
You could enhance this script by:
- Sending a Pushover notification on IP change
- Logging to syslog instead of a flat file
- Supporting IPv6 (AAAA record)
- Adding error handling if Cloudflare API is unreachable
Use Cases
This script is useful for:
- Hosting services behind dynamic residential IP
- WireGuard endpoints
- Self-hosted dashboards
- Proxmox remote access
- Home VPN servers
Tested On
- Debian 12
- Proxmox VE 8
- Cloudflare DNS (A records)