Skip to content

Everything for WordPress, web development — and beyond

🚀 How to set up dynamic DNS through Cloudflare on Raspberry Pi

🚀 How to set up dynamic DNS through Cloudflare on Raspberry Pi

Your home server on Raspberry Pi runs 24/7, but your ISP changes the external IP every day. Manually knocking on the new address is a job for an archaeologist. And DynDNS and No-IP bombard you with emails: "log in once a month or we'll disable your free account."

Turns out, Cloudflare can work as dynamic DNS, without promotional emails, without update frequency limits, and without third-party subdomains. Already have a domain on Cloudflare? Then all you need is an API token and a bash script on Raspberry Pi. In half an hour you'll set up a scheme that automatically updates the A-record whenever the IP changes.

And if you want not just "to get into the console," but deploy a full-fledged development site, even on WordPress, this setup becomes the foundation: you get access to your server from anywhere in the world via a nice subdomain like home.example.com.

💡 Quick overview:

  • Step 1: Create an A-record in Cloudflare's DNS panel with test IP 0.0.0.0
  • Step 2: Get an API token (not a global key, tokens are safer and revocable with one click)
  • Step 3: Run a bash script that queries the current IP and pushes it to Cloudflare API
  • Step 4: Set up a cron job and forget about manual updates forever

What you'll need

  • A domain added to Cloudflare and managed through their DNS
  • Raspberry Pi (any model works, Pi 3, Pi 4, Pi Zero 2 W) with Raspbian or Raspberry Pi OS
  • Static local IP for Raspberry Pi (configured in OS GUI or via router)
  • Terminal access to Pi via SSH or directly

What services exactly you host on Raspberry Pi and how you forward ports on the router is a separate topic beyond this tutorial. The scheme works identically whether for a web server with WordPress, VNC access, or a surveillance camera.

1. Create an A-record for your home server

Choose a subdomain, it will become your permanent entry point. I use home, but you can pick any: dev, lab, cam.

  • Go to Cloudflare dashboard and select your domain
  • Navigate to the DNS tab
  • Click Add record, type A
  • In the Name field enter the subdomain (for example, home)
  • In the **IPv4 **address field specify 0.0.0.0, temporarily, to test the script later
  • Leave Proxy status off (gray cloud), caching and proxy aren't needed for DDNS
A-record home with test IP 0.0.0.0 in Cloudflare DNS

Record is ready. Now let's get the API access key.

2. Get Cloudflare API token

Previously all guides suggested the global API key, which gives full access to all account domains. This is dangerous: if the key leaks, everything leaks. Now Cloudflare recommends API tokens with granular permissions. A token can be limited to a specific domain and action, and in case of compromise, revoked with one click.

  • In Cloudflare dashboard go to: My ProfileAPI Tokens
  • Click Create Token
  • Select the Edit zone DNS template (or create a custom one)
  • In Zone Resources section specify the particular domain you're configuring DDNS for
  • Click Continue to summaryCreate Token
  • Copy the token, it's shown only once

The token looks like a long string starting with cfut_. Store it in a safe place, the script on Raspberry Pi will need it in a minute.

3. Auto-update script on Raspberry Pi

Connect to Raspberry Pi via SSH or open terminal. Let's create a folder for the script:

1mkdir -p ~/cf && cd ~/cf

Now, the script itself. Create file cloudflare-ddns.sh:

1nano ~/cf/cloudflare-ddns.sh

Copy the following code into it:

1#!/bin/bash
2
3 ## Settings (replace with yours)
4AUTH_EMAIL="[email protected]"
5API_TOKEN="cfut_your_token"
6ZONE_NAME="example.com"
7RECORD_NAME="home.example.com"
8
9 ## Get current public IP
10CURRENT_IP=$(curl -s https://api.ipify.org)
11
12 ## Get Zone ID
13ZONE_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$ZONE_NAME" \
14 -H "Authorization: Bearer $API_TOKEN" \
15 -H "Content-Type: application/json" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
16
17 ## Get Record ID and current record IP
18RECORD=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$RECORD_NAME" \
19 -H "Authorization: Bearer $API_TOKEN" \
20 -H "Content-Type: application/json")
21
22RECORD_ID=$(echo "$RECORD" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
23DNS_IP=$(echo "$RECORD" | grep -o '"content":"[^"]*"' | head -1 | cut -d'"' -f4)
24
25 ## Update record if IP changed
26if [ "$CURRENT_IP" != "$DNS_IP" ]; then
27 UPDATE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
28 -H "Authorization: Bearer $API_TOKEN" \
29 -H "Content-Type: application/json" \
30 --data "{\"type\":\"A\",\"name\":\"$RECORD_NAME\",\"content\":\"$CURRENT_IP\",\"ttl\":120}")
31
32 echo "$(date): IP changed from $DNS_IP to $CURRENT_IP — updated" >> ~/cf/cloudflare-ddns.log
33else
34 echo "$(date): IP unchanged ($CURRENT_IP)" >> ~/cf/cloudflare-ddns.log
35fi

Replace the four variables in the "Settings" block with your real values. Email, the same one for Cloudflare login. Token, the one you copied in step 2. Zone name, your domain without http://. Record name, the full subdomain.

Save the file (Ctrl+O, Enter, Ctrl+X in nano) and make it executable:

1chmod +x ~/cf/cloudflare-ddns.sh

Run manually to check:

1~/cf/cloudflare-ddns.sh

If everything is configured correctly, go to Cloudflare DNS panel, the A-record IP address should change from 0.0.0.0 to your real public IP. The log is written to ~/cf/cloudflare-ddns.log, check there if something went wrong.

4. Automation via cron

Manual launch is only for testing. From here cron takes over.

1crontab -e

If the system asks you to choose an editor, pick nano (usually number 2, it's the simplest). Add a line at the end of the file:

10 */6 * * * /bin/bash /home/pi/cf/cloudflare-ddns.sh

This entry runs the script every 6 hours. Even if the ISP changes IP once every few months, a six-hour window is a reasonable compromise between timeliness and API load.

If you want more frequently, replace with */30 * * * * (every 30 minutes). But remember: Cloudflare API free limit is 1200 requests per minute, so even minute-by-minute calls aren't scary for one domain.

Save (Ctrl+X, Y, Enter). Cron will pick up changes automatically, no need to restart the service. Help with decoding the asterisks at crontab.guru.

Short video guide on this same topic, from NetworkChuck, with demonstration on real Raspberry Pi and explanation of each step:

What to do if the script didn't find Zone ID or Record ID

Sometimes the API doesn't return identifiers automatically, especially if the account has many domains or records with similar names. Then get them manually:

  • Go to Cloudflare dashboard, select the domain, DNS tab
  • Open browser developer tools (F12Network tab → XHR subtab)
  • Click on the cloud icon next to your A-record, a request to the API will appear in the network panel
  • Right-click on it → CopyCopy link address (Chrome) or Copy URL (Firefox)
  • In the copied link find the part after zones/, that's the Zone ID, after dns_records/, the Record ID

Substitute them into ZONE_ID and RECORD_ID variables in the script, and everything will work. Make sure the cloud in DNS panel is gray (proxy off): Cloudflare shouldn't cache IP for DDNS.

⁉️🤔 Frequently asked questions

Is it mandatory to use API token instead of global key?

The global key still works, but the token is the modern and secure option. A token can be limited to one domain and DNS editing rights. The global key gives access to the entire account. If the script or Pi are compromised, you simply revoke the token, whereas you'd have to change the key everywhere.

How does this approach differ from DuckDNS or No-IP?

Third-party DDNS services give a third-level subdomain like myhome.duckdns.org. The Cloudflare approach uses your own domain (home.example.com), this looks more professional, doesn't depend on someone else's service, and doesn't require monthly login for activity confirmation. Plus you get access to the entire Cloudflare ecosystem: DDoS protection, SSL certificates, firewall rules.

Do I need to forward ports on the router?

Yes, to reach Raspberry Pi from the internet. DDNS only ensures the domain always points to the current IP. But for traffic from that IP to reach the Pi, you need to configure port forwarding on the router for the necessary ports (22 for SSH, 80/443 for web, and so on).

What to do if Raspberry Pi is behind ISP NAT?

Some ISPs use Carrier-Grade NAT (CGNAT), then you don't have your own public IP and DDNS won't help. Solution: request a white IP from the ISP (often free by request) or use Cloudflare Tunnel, which creates an encrypted channel from Pi to Cloudflare without port forwarding.

How much does this solution cost?

Zero. Cloudflare DNS is free for unlimited A-records. You already bought the Raspberry Pi. Power consumption of Pi 3/4 under load is about 3-5 W, annually that's roughly $250-400.

Can I update multiple subdomains?

Yes, duplicate the blocks with RECORD_NAME and DNS_IP in the script for each subdomain or wrap the logic in a loop over an array of names.

Dynamic DNS on Cloudflare: what to set up in 2026

The scheme with Cloudflare API and cron has survived several generations of Raspberry Pi and hasn't lost relevance. You get your own subdomain, don't depend on third-party DDNS providers, and don't pay a penny.

If you want to go further, look into ready-made Docker images: oznu/cloudflare-ddns updates IP every 5 minutes and is configured via environment variables. For those who prefer Python, there's aribasadme/rpi-cloudflare-ddns, tailored specifically for Raspberry Pi.

The main thing you did today: Raspberry Pi is now accessible from anywhere in the world at a permanent address. And this is the foundation for anything: from a WordPress dev server to your own cloud storage.

🔗 Cloudflare DNS API documentation