Synology + Cloudflare DNS - Dynamic IP Updates Without Tears (or Cronjobs)
If you run a Synology NAS at home and expose services through your own domain, you’ve probably met the classic villain: your ISP changes your public IP. One moment your domain points to your NAS, the next moment it points to… yesterday.
In this setup we solved that by building a tiny Cloudflare Worker that acts as a “DDNS gateway”. Synology DSM can only call a simple GET URL for DDNS updates, but Cloudflare’s DNS API wants authenticated requests. So the Worker translates DSM’s “DDNS ping” into a proper Cloudflare DNS update.
No cronjobs. No third-party DDNS services. And no more wondering why your NAS suddenly moved to a different IP overnight.
Prerequisites
- A Synology NAS with DSM admin access
- Your domain’s authoritative DNS is hosted on Cloudflare(the registrar does not matter as long as Cloudflare nameservers are used)
- A Cloudflare account with access to the zone and Workers
- A terminal with curl and openssl
- An existing DNS record you want to update
(this guide updates the root record @ → somedomain.xy)
Step 1: Generate a DDNS secret token
This token is not a Cloudflare API token.
It is a private shared secret between DSM and the Worker.
openssl rand -hex 32
Save the output somewhere safe (a password manager is a very good idea).
We will refer to this value as:
- DDNS_TOKEN
Step 2: Create a Cloudflare API token for DNS updates
In the Cloudflare dashboard:
- Go to My Profile → API Tokens → Create Token
- Create a custom token with:
- Permissions: Zone → DNS → Edit
- Zone Resources: Include → Specific zone → your domain
- Create the token and copy it
We will refer to this value as:
- CF_API_TOKEN
This token will live only inside the Worker, never on the NAS.
Step 3: Retrieve the Zone ID
You can copy the Zone ID from the Cloudflare domain overview, or fetch it via the API.
First, store your Cloudflare API token locally:
read -s CF_API_TOKEN
Paste the token and press Enter.
Now query the API:
curl -s \
-H "Authorization: Bearer $CF_API_TOKEN" \
"https://api.cloudflare.com/client/v4/zones?name=somedomain.xy" \
| python3 -m json.tool
Look for:
result[0].id
That value is your:
- ZONE_ID
Step 4: Retrieve the DNS Record ID
For the root record (@), Cloudflare’s API uses the full domain name.
curl -s \
-H "Authorization: Bearer $CF_API_TOKEN" \
"https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records?type=A&name=somedomain.xy" \
| python3 -m json.tool
Replace ZONE_ID with the real value from the previous step.
From the response, copy:
result[0].id
That value is your:
- RECORD_ID
Also note:
- HOSTNAME should be somedomain.xy (not@)
Step 5: Create the Cloudflare Worker
In Cloudflare:
- Go to Workers & Pages → Create application → Worker
Paste the following Worker code:
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Expected query parameters:
// /update?token=...&ip=...
const reqToken = url.searchParams.get("token");
const ip = url.searchParams.get("ip");
// Authenticate DSM
if (!reqToken || reqToken !== env.DDNS_TOKEN) {
return new Response("unauthorized", { status: 401 });
}
if (!ip) {
return new Response("missing ip", { status: 400 });
}
// Update DNS record via Cloudflare API
const res = await fetch(
`https://api.cloudflare.com/client/v4/zones/${env.ZONE_ID}/dns_records/${env.RECORD_ID}`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${env.API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "A",
name: env.HOSTNAME,
content: ip,
ttl: 120,
proxied: false,
}),
}
);
const data = await res.json();
if (!data.success) {
return new Response(JSON.stringify(data), { status: 500 });
}
return new Response("ok", { status: 200 });
},
};
Deploy the Worker.
Step 6: Configure Worker environment variables
Open the Worker settings and add the following encrypted variables:
- API_TOKEN = CF_API_TOKEN
- DDNS_TOKEN = token from Step 1
- ZONE_ID = value from Step 3
- RECORD_ID = value from Step 4
- HOSTNAME = somedomain.xy
Save and deploy.
Step 7: Test the Worker manually
Your Worker will be reachable at a URL similar to:
https://your-cloudflare-worker-name.mika-stumborg.workers.dev
Test it with a known IP:
curl -i \
"https://stumborg-server-ip-update.mika-stumborg.workers.dev/update?token=YOUR_DDNS_TOKEN&ip=1.2.3.4"
Expected result:
- HTTP 200
- Response body: ok
Verify DNS:
dig @1.1.1.1 stumborg.de A +short
Expected output:
1.2.3.4 <- YOUR IP
If you still see the old IP for a moment, congratulations — you just met DNS caching.
Step 8: Configure DSM (Custom DDNS provider)
DSM path:
Control Panel → External Access → DDNS
Step 8.1: Create a custom provider
Click Customize provider and add a new one.
Query URL:
https://stumborg-server-ip-update.mika-stumborg.workers.dev/update?token=__PASSWORD__&ip=__MYIP__
Notes:
- PASSWORD is replaced by DSM with the “Password / Key” field
- MYIP is replaced by DSM with the detected public IP
Save the provider.
Step 8.2: Create the DDNS entry
Add a new DDNS entry with:
- Service provider: your custom provider (for example Cloudflare-Worker)
- Hostname: somedomain.xy
- Username / Email: any value (for example ddns)
- Password / Key: DDNS_TOKEN
- External address: automatic
Click Test Connection.
Expected result:
- Status: Normal
Step 9: A note on renaming Workers
If you use the workers.dev URL directly and rename the Worker, the URL changes.
DSM will keep calling the old URL and DDNS updates will fail silently.
Fix:
- Update the Query URL in DSM to the new Worker URL.
Recommended long-term solution:
- Create a stable route such as:
https://ddns.somedomain.xy/update
- Route that hostname to the Worker.
This way DSM never needs to change, even if the Worker is renamed or replaced.
Troubleshooting
DSM test fails
- Verify the Query URL under Customize provider
- Double-check the Worker URL after renaming
Worker returns 401 unauthorized
- DDNS_TOKEN in DSM does not match the Worker variable
Worker returns 500 with authentication errors
- API_TOKEN lacks Zone → DNS → Edit
- Token is not scoped to the correct zone
Conclusion:
You now have:
- A Synology NAS that automatically reports IP changes
- A Cloudflare Worker that securely updates DNS records
- A domain that always points to your current public IP
No cronjobs, no third-party DDNS services, and no late-night debugging sessions wondering why your NAS disappeared.
From here, you can easily extend this setup with IPv6 (AAAA records), multiple hostnames, or automatic IP detection inside the Worker using request headers.