Introduction
Some hosting providers offer small VPS instances behind dynamic NAT without providing a DDNS service. In such cases, we need to set one up ourselves.
This is a beginner-friendly tutorial that utilizes Cloudflare. Before you begin, please ensure your domain is already managed by Cloudflare.
This script only implements DDNS for IPv4. On a side note, I’ve yet to encounter dynamic IPv6 addresses anyway.
Configure a Cloudflare API Token
Find Your Zone ID
Navigate to your domain’s dashboard on Cloudflare. On the right-hand side, scroll down to the API section and find your Zone ID. Make a note of it.

Create an API Token
Go to My Profile -> API Tokens.

Create a new API token with the following permissions:

Once created, make a note of your API Token.
Add an A Record
In your Cloudflare DNS panel, add a new A record. You can set its initial value to a placeholder like 1.1.1.1. The script will update it later.
Update the DNS Record and Configure Cron
The DDNS Script
Create a new script file and name it cf-ddns.sh (or any other name you prefer).
#!/bin/bash
# --- CONFIGURATION ---
ZONE_ID="your_zone_id_here"
API_TOKEN="your_api_token_here"
RECORD_NAME="home.example.com"
# --- END CONFIGURATION ---
IP=$(curl -s https://ipv4.icanhazip.com)
echo "Current IP: $IP"
JSON_DATA=$(curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=$RECORD_NAME" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json")
RECORD_ID=$(echo $JSON_DATA | jq -r '.result[0].id')
if [ "$RECORD_ID" == "null" ] || [ -z "$RECORD_ID" ]; then
echo "Error: Could not find Record ID for $RECORD_NAME"
echo "Cloudflare Response: $JSON_DATA"
exit 1
fi
echo "Record ID: $RECORD_ID"
RESPONSE=$(curl -s -X PUT \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
--data "{
\"type\":\"A\",
\"name\":\"$RECORD_NAME\",
\"content\":\"$IP\",
\"ttl\":120,
\"proxied\":false
}")
SUCCESS=$(echo $RESPONSE | jq -r '.success')
if [ "$SUCCESS" == "true" ]; then
echo "$(date): Update success! IP set to $IP"
else
echo "$(date): Update failed!"
echo "Response: $RESPONSE"
fi
Note: The script uses
jqto parse JSON. If you don’t have it installed, run:apt install jq -y.
Make the script executable: chmod +x /root/cf-ddns.sh
Run the script once to test it: ./cf-ddns.sh. If it’s successful, you should see a response containing "success":true.
Add a Cron Job
To run the script automatically, edit your crontab file:
crontab -e
Add the following line to run the script every five minutes and log the output:
*/5 * * * * /root/cf-ddns.sh >> /var/log/cf-ddns.log 2>&1
This example uses a 5-minute interval, but you can change it to every 2 minutes (*/2 * * * *) or another value if you wish.
After the script runs, you’ll see that the A record in your Cloudflare dashboard has been updated to your server’s current IP address.