Skip to main content

Command Palette

Search for a command to run...

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

Updated
7 min read
How to Configure Cloudflare as a Dynamic DNS (DDNS) on Ubuntu Server 24.04 LTS
K

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?

BenefitDescription
Your Own DomainUse server.yourdomain.com instead of something.duckdns.org
Free ForeverNo 30-day renewal reminders like other free DDNS providers
Fast DNSCloudflare has one of the fastest DNS networks globally
Extra FeaturesOptional 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

  • curl installed (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.

  1. Log into your Cloudflare Dashboard

  2. Select your domain

  3. Go to DNSRecords

  4. 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

  5. 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.

  1. Go to Cloudflare API Tokens

  2. Click Create Token

  3. Click Use template next to Edit zone DNS

  4. Configure:

    • Permissions: Zone - DNS - Edit

    • Zone Resources: Include → Specific zone → Select your domain

  5. Click Continue to summaryCreate Token

  6. Copy the token immediately — you won't see it again!


Step 3: Get Your Zone ID

  1. Go to your domain's Overview page in Cloudflare

  2. Scroll down on the right sidebar

  3. 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 2

  • YOUR_ZONE_ID — your Zone ID from Step 3

  • server.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

CommandDescription
sudo systemctl status cloudflare-ddns.timerCheck timer status
sudo systemctl list-timers cloudflare-ddns.timerSee next/last run time
sudo systemctl start cloudflare-ddns.serviceManually trigger update
cat /var/log/cloudflare-ddns.logView update history
sudo systemctl stop cloudflare-ddns.timerStop automatic updates
sudo systemctl disable cloudflare-ddns.timerDisable 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_NAME matches 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

FilePurpose
/opt/cloudflare-ddns/update.shMain update script
/var/log/cloudflare-ddns.logLog file
/etc/systemd/system/cloudflare-ddns.serviceSystemd service
/etc/systemd/system/cloudflare-ddns.timerSystemd timer
/tmp/cloudflare-ddns-ip.cacheIP 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!

More from this blog

K

Kusal Tharindu

15 posts

Passionate DevOps Engineer and blogger, aiming to demystify complex DevOps concepts. Dedicated to assisting the community with practical, everyday insights.