How to Configure Cloudflare as a Dynamic DNS (DDNS) on Ubuntu Server 24.04 LTS

As a dedicated DevOps Engineer, I've immersed myself in the dynamic world of DevOps, sharing my insights through blogs to support the community. I aim to simplify complex processes, empowering both beginners and experts to navigate DevOps with confidence and ease, fostering collective growth in this ever-evolving field.
Introduction
If you're running a home server and want to access it remotely, you've probably faced this frustrating problem: your ISP keeps changing your public IP address. Every time your router restarts or the IP lease expires, you lose access to your server.
The solution? Dynamic DNS (DDNS) — a service that automatically updates your domain name to point to your current IP address.
In this guide, I'll show you how to use Cloudflare as your DDNS provider. If you already own a domain on Cloudflare, this is the cleanest and most professional solution.
Why Use Cloudflare for DDNS?
| Benefit | Description |
| Your Own Domain | Use server.yourdomain.com instead of something.duckdns.org |
| Free Forever | No 30-day renewal reminders like other free DDNS providers |
| Fast DNS | Cloudflare has one of the fastest DNS networks globally |
| Extra Features | Optional DDoS protection and CDN for web services |
Prerequisites
Before starting, make sure you have:
✅ Ubuntu Server 24.04 LTS installed
✅ A domain name managed by Cloudflare
✅ Root or sudo access to your server
✅
curlinstalled (sudo apt install curl -y)
Step 1: Create a DNS Record in Cloudflare
First, we need to create the DNS record that will be updated automatically.
Log into your Cloudflare Dashboard
Select your domain
Go to DNS → Records
Click Add Record and enter:
Type: A
Name:
server(or your preferred subdomain)IPv4 address:
1.2.3.4(temporary — will be updated by script)Proxy status: DNS only (grey cloud) ⚠️ Important for SSH access
TTL: Auto
Click Save
You now have a record like server.yourdomain.com.

Note: Keep the proxy status as "DNS only" (grey cloud) if you need direct access for SSH, gaming servers, or other non-HTTP services.
Step 2: Create a Cloudflare API Token
The script needs permission to update your DNS records. We'll create a restricted API token for security.
Go to Cloudflare API Tokens
Click Create Token
Click Use template next to Edit zone DNS
Configure:
Permissions: Zone - DNS - Edit
Zone Resources: Include → Specific zone → Select your domain
Click Continue to summary → Create Token
Copy the token immediately — you won't see it again!

Step 3: Get Your Zone ID
Go to your domain's Overview page in Cloudflare
Scroll down on the right sidebar
Copy the Zone ID (32-character string)

Save both the API Token and Zone ID — you'll need them shortly.
Step 4: Create the DDNS Script Directory
Now let's set up the server. Connect via SSH and run:
sudo mkdir -p /opt/cloudflare-ddns
This creates a dedicated directory for our DDNS script.
Step 5: Create the Update Script
Create the main script file:
sudo nano /opt/cloudflare-ddns/update.sh
Paste the following content:
#!/bin/bash
# Cloudflare DDNS Update Script
# =============================
# Configuration - UPDATE THESE VALUES
CF_API_TOKEN="YOUR_CLOUDFLARE_API_TOKEN"
CF_ZONE_ID="YOUR_ZONE_ID"
CF_RECORD_NAME="server.yourdomain.com"
# =============================
# Do not edit below this line
# =============================
LOG_FILE="/var/log/cloudflare-ddns.log"
IP_CACHE="/tmp/cloudflare-ddns-ip.cache"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# Get current public IP (force IPv4)
CURRENT_IP=$(curl -4 -s https://api.cloudflare.com/cdn-cgi/trace | grep -oP 'ip=\K[^\s]+')
if [[ -z "$CURRENT_IP" ]]; then
log "ERROR: Could not determine public IP"
exit 1
fi
# Check cached IP
if [[ -f "$IP_CACHE" ]]; then
CACHED_IP=$(cat "$IP_CACHE")
if [[ "$CURRENT_IP" == "$CACHED_IP" ]]; then
# IP hasn't changed, no update needed
exit 0
fi
fi
# Get the DNS record ID
RECORD_INFO=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records?type=A&name=${CF_RECORD_NAME}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json")
RECORD_ID=$(echo "$RECORD_INFO" | grep -oP '"id":"\K[^"]+' | head -1)
RECORD_IP=$(echo "$RECORD_INFO" | grep -oP '"content":"\K[^"]+' | head -1)
if [[ -z "$RECORD_ID" ]]; then
log "ERROR: Could not find DNS record for ${CF_RECORD_NAME}"
exit 1
fi
# Check if update is needed
if [[ "$CURRENT_IP" == "$RECORD_IP" ]]; then
echo "$CURRENT_IP" > "$IP_CACHE"
log "INFO: IP unchanged ($CURRENT_IP)"
exit 0
fi
# Update the DNS record
UPDATE_RESULT=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records/${RECORD_ID}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"A\",\"name\":\"${CF_RECORD_NAME}\",\"content\":\"${CURRENT_IP}\",\"ttl\":1,\"proxied\":false}")
if echo "$UPDATE_RESULT" | grep -q '"success":true'; then
echo "$CURRENT_IP" > "$IP_CACHE"
log "SUCCESS: Updated ${CF_RECORD_NAME} from ${RECORD_IP} to ${CURRENT_IP}"
echo "DNS updated successfully: ${CURRENT_IP}"
else
log "ERROR: Failed to update DNS - $UPDATE_RESULT"
exit 1
fi
Important: Replace these three values at the top:
YOUR_CLOUDFLARE_API_TOKEN— your API token from Step 2YOUR_ZONE_ID— your Zone ID from Step 3server.yourdomain.com— your actual hostname from Step 1
Save and exit: Ctrl + O, Enter, Ctrl + X
Step 6: Secure the Script
Since the script contains your API token, we need to restrict access:
sudo chmod 700 /opt/cloudflare-ddns/update.sh
This allows only root to read, write, and execute the script.
Step 7: Test the Script
Run a manual test:
sudo /opt/cloudflare-ddns/update.sh
Expected output:
DNS updated successfully: 123.45.67.89
Verify by checking your Cloudflare dashboard — the A record should now show your current IP.
Step 8: Create a Systemd Service
Create a service file so systemd knows how to run the script:
sudo nano /etc/systemd/system/cloudflare-ddns.service
Paste this content:
[Unit]
Description=Cloudflare DDNS Update
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/cloudflare-ddns/update.sh
Save and exit.
Step 9: Create a Systemd Timer
Create a timer to run the service every 5 minutes:
sudo nano /etc/systemd/system/cloudflare-ddns.timer
Paste this content:
[Unit]
Description=Run Cloudflare DDNS update every 5 minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
AccuracySec=1min
[Install]
WantedBy=timers.target
Save and exit.
Step 10: Enable and Start the Timer
Activate the automation:
# Reload systemd to recognize new files
sudo systemctl daemon-reload
# Enable timer to start on boot
sudo systemctl enable cloudflare-ddns.timer
# Start the timer now
sudo systemctl start cloudflare-ddns.timer
Step 11: Verify Everything is Working
Check the timer status:
sudo systemctl status cloudflare-ddns.timer
Expected output:
● cloudflare-ddns.timer - Run Cloudflare DDNS update every 5 minutes
Loaded: loaded (/etc/systemd/system/cloudflare-ddns.timer; enabled)
Active: active (waiting)
Trigger: Sun 2026-01-18 18:07:31 UTC; 4min left
View all timers with next run time:
sudo systemctl list-timers cloudflare-ddns.timer
Check the log file:
cat /var/log/cloudflare-ddns.log
Useful Commands Reference
| Command | Description |
sudo systemctl status cloudflare-ddns.timer | Check timer status |
sudo systemctl list-timers cloudflare-ddns.timer | See next/last run time |
sudo systemctl start cloudflare-ddns.service | Manually trigger update |
cat /var/log/cloudflare-ddns.log | View update history |
sudo systemctl stop cloudflare-ddns.timer | Stop automatic updates |
sudo systemctl disable cloudflare-ddns.timer | Disable on boot |
Troubleshooting
Error: "Content for A record must be a valid IPv4 address"
Your server is detecting an IPv6 address instead of IPv4. Make sure the script uses -4 flag with curl:
CURRENT_IP=$(curl -4 -s https://api.cloudflare.com/cdn-cgi/trace | grep -oP 'ip=\K[^\s]+')
Error: "Could not find DNS record"
Verify
CF_RECORD_NAMEmatches exactly what's in Cloudflare (including domain)Check that your API token has permissions for the correct zone
Error: "Could not determine public IP"
Check your internet connection
Try:
curl -4 -s https://api.cloudflare.com/cdn-cgi/trace
File Locations Summary
| File | Purpose |
/opt/cloudflare-ddns/update.sh | Main update script |
/var/log/cloudflare-ddns.log | Log file |
/etc/systemd/system/cloudflare-ddns.service | Systemd service |
/etc/systemd/system/cloudflare-ddns.timer | Systemd timer |
/tmp/cloudflare-ddns-ip.cache | IP cache (avoids unnecessary updates) |
Conclusion
You now have a fully automated DDNS setup using Cloudflare! Your domain will always point to your home server's current IP address, checking every 5 minutes.
This solution is:
Free — no subscription or renewal required
Reliable — uses Cloudflare's robust API
Secure — API token is protected with restricted file permissions
Professional — uses your own domain name
Now you can access your home server from anywhere using ssh user@server.yourdomain.com!
Next Steps
Configure port forwarding on your router for SSH (port 22) and HTTP (port 80/443)
Consider using a non-standard SSH port for added security
Set up SSL certificates with Let's Encrypt for HTTPS
If you found this guide helpful, feel free to share it with others facing the same challenge!





