Free DDNS with Cloudflare

DNS Logo

Since my domain is registered with GoDaddy, I originally used their API for DDNS. However, after they restricted API access for smaller clients, I had to find an alternative. Fortunately, Cloudflare offers a free API for managing DNS records. Even better, they allow you to keep your domain with your current registrar; you simply update your name servers to Cloudflare’s and manage your domain through their dashboard instead.

Table of Contents
    Add a header to begin generating the table of contents

    Why I Choose DDNS Over Cloudflare Tunnels

    I prefer using Cloudflare strictly for DDNS rather than their Tunnel solution. My goal is to remain independent of their infrastructure’s reliability—especially following recent service outages.

    By opting for DDNS, I’ve taken security into my own hands. While this requires more manual configuration, it ensures that my connectivity isn’t tied to a single provider’s uptime. To help you decide which path is right for your setup, I have provided a comparison table below.

    FeatureCloudflare DDNS (Bash Script)Cloudflare Tunnel (cloudflared)
    DependencyLow. Only relies on Cloudflare for DNS resolution.High. Relies on Cloudflare’s infrastructure to route every packet.
    Failure ModeIf Cloudflare is down, cached DNS might still work; Direct IP access is still possible.If Cloudflare is down, your external URL is dead. Period.
    ControlYou own the traffic flow and the SSL certificates.Cloudflare owns the connection; they can (and do) inspect traffic.
    SecurityManaged by CloudflareManaged by You (Firewall/VPN)
    PrivacyYou can use “DNS Only” (Grey Cloud) to keep Cloudflare out of your data.All traffic must pass through their servers unencrypted at the edge.
    Setup EffortHigher (Requires port forwarding + local Reverse Proxy).Lower (One container, no router config).

    Cloudflare configuration

    Create API token

    First of all, you have to set up your domain on Cloudflare following their documentation.

    The next step is obtaining API keys for your script:

    • Log into Cloudflare and click on the “Quick Search” field on the left panel.
    • Type “token” keyword in the search field of a new window and click on API Tokens.
    Quick search window
    • Now, click on the “Create Token” button, find “Edit zone DNS” template, and click on “USE template”.
    • Set up “Edit” permissions for your new token, and include all zones or a specific one.
    • Click “Continue to summary”.
    • Click on “Create token”.
    Token settings

    Finally, you’ll see your new token and a curl command for checking if it works.

    DNS Zone ID

    Back to your account overview screen, scroll it down and find Zone ID code in the bottom-right corner.

    Bash Script

    Now, you have all nessessary information to create a script.

    First of all, install dnsutils with the nslookup tool and the jq tool:

    sudo apt-get update && sudo apt-get install dnsutils jq -y

    Next, create the file and make it excecutable:

    sudo touch /opt/update_my_ip.sh
    sudo chmod +x /opt/update_my_ip.sh

    Open it in your preferred text editor e.g. mcedit, and add the following script:

    #!/bin/bash
    
    myrecord="YOUR_DNS_RECORD"
    
    APItocken="YOUR_API_TOKEN"
    ZONE_ID="YOUR_ZONE_ID"
    
    myip=`/usr/bin/curl -s "https://api.ipify.org"`
    dnsip=`/usr/bin/nslookup $myrecord 8.8.8.8 | tail -n 2 | grep Address`
    
    dnsip=${dnsip##*' '}
    
    echo "MyIP: $myip"
    echo "CurrentDNS record: $dnsip"
    
    if [ "$dnsip" != "$myip" ]; then
    
    echo "IP is changed to: $myip"
    
    recordID=`curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=$myrecord" \
    -H "Authorization: Bearer $APItocken" \
    -H "Content-Type: application/json" | jq -r '.result[0].id'`
    
    
    curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$recordID" \
    -H "Authorization: Bearer $APItocken" \
    -H "Content-Type: application/json" \
    --data "{\"type\":\"A\",\"name\":\"$myrecord\",\"content\":\"$myip\",\"ttl\":120,\"proxied\":false}"
    
    fi

    ⚠️ Explanation:

    1. The Configuration (Variables)

    At the top, you define your specific Cloudflare credentials.

    • myrecord: The domain or subdomain you want to update (e.g., ha.yourdomain.com).

    • APItocken: Your secret Cloudflare API Token.

    • ZONE_ID: The unique identifier for your domain’s zone in Cloudflare.

    2. The Discovery Phase

    Before the script does anything, it needs to compare two things: your actual current IP and what Cloudflare thinks your IP is.

    • Fetching Local IP: It uses curl to ask api.ipify.org for your public-facing IP address.

    • Checking DNS Record: It uses nslookup to ask Google’s DNS (8.8.8.8) what the current record is for your domain.

    • Cleaning the Data: The line dnsip=${dnsip##*' '}  Bash string manipulation that strips away the text and leaves only the raw IP address.

    Cloudflare is very generous with their free tier, but they don’t like being spammed. If your script sends an update request every few minutes when the IP hasn’t actually changed, Cloudflare’s security systems might flag your IP for Rate Limiting. In extreme cases, they could temporarily block your API access.

    3. The Comparison & Update

    This is the “brain” of the script.

    • The If-Statement: It compares $dnsip to $myip. If they are the same, the script ends—no work needed! If they are different, it proceeds to the update.

    • Fetching the Record ID: Cloudflare needs a specific record_id (not just the name) to perform an update. The script sends a GET request to find this ID.

    • The PATCH Request: Finally, it sends a PATCH request to Cloudflare. This updates the “A” record with your new IP.

      • ttl: 120: Sets a short “Time to Live” so the change propagates quickly.

      • proxied: false: This keeps the Cloudflare proxy off.

    Running Script by Scchedule

    The last step is configuring cron to run the script by schedule.

    Let’s create a cron file and set up the right permissions:

    sudo touch /etc/cron.d/updateip
    sudo chmod 0644 /etc/cron.d/updateip

    Open it in your preferred text editor e.g. mcedit, and add the following config:

    # Example of job definition:
    # .---------------- minute (0 - 59)
    # |  .------------- hour (0 - 23)
    # |  |  .---------- day of month (1 - 31)
    # |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
    # |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
    # |  |  |  |  |
    # *  *  *  *  * user-name command to be executed
    
    */10 * * * * root /opt/update_my_ip.sh > /dev/null
     

    ⚠️ Tips:

    • Adjust scheduling according to your preferences. 
    • The last EMPTY row is MANDATORY, otherwise cron daemon won’t apply your config.

    Restart cron daemon to apply the config:

    sudo systemctl restart cron

    Now you have your free DDNS!!!