Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungThe Indie Dev Visibility Playbook: From Zero Users to Your First 100(21.09.2026 um 00:08 Uhr)
Sichere ProgrammierungBase, Chat and Reasoning Models: How Are They Different?(21.09.2026 um 00:09 Uhr)
Sichere ProgrammierungHexfield Deck is for Kanban lovers and Markdown believers(21.09.2026 um 00:20 Uhr)
Sichere ProgrammierungPermissions and Authorisation: A Practical Playbook(21.09.2026 um 00:21 Uhr)
Linux Tipps & Hardeningfilet | Terminal File Manager(20.09.2026 um 21:03 Uhr)
Linux Tipps & HardeningLooking for feedback on my Linux Distro(20.09.2026 um 21:03 Uhr)
Sichere ProgrammierungThe Indie Dev Visibility Playbook: From Zero Users to Your First 100(21.09.2026 um 00:08 Uhr)
Sichere ProgrammierungBase, Chat and Reasoning Models: How Are They Different?(21.09.2026 um 00:09 Uhr)
Sichere ProgrammierungHexfield Deck is for Kanban lovers and Markdown believers(21.09.2026 um 00:20 Uhr)
Sichere ProgrammierungPermissions and Authorisation: A Practical Playbook(21.09.2026 um 00:21 Uhr)
Linux Tipps & Hardeningfilet | Terminal File Manager(20.09.2026 um 21:03 Uhr)
Linux Tipps & HardeningLooking for feedback on my Linux Distro(20.09.2026 um 21:03 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Dynamic DNS sync with Cloudflare

Reagiere als Erste:r — dein Feedback zählt!

Dynamic DNS sync with Cloudflare

If you ever get yourself a Raspberry Pi, NAS, or turn an old computer into a server, you start to think about accessing it from outside of your network. This usually requires you to open a port on your router and forward to the IP of your device. Some of the services you might want to access could be a Wireguard/OpenVPN VPN or some other service.

One note about VPN, there is a way to get into your network without opening a port. You can use https://tailscale.com/ to open a tunnel to your network and access it with better security. However, using Tailscale doesn’t work with all use cases. In this blog post, I go over how to automatically sync your public IP with Cloudflare and always have your Dynamic DNS domain point to the correct IP address.

Also those of you who have experience with Cloudflare and using their proxy, you will know that Cloudflare only will proxy certain ports and the port you have for your VPN or other service might not work. If you need to have a dedicate subdomain for VPN, you can modify the code below and have it update two records. That's how I have setup.

Another note on syncing IPs with cloudflare: there are already some tools that can do this for you. I went this path, because I wanted to understand how it works under the hood and it was fun! If you are interested in writing some code to automate some tasks, this post is for you!

Assumptions

  • Your domain is on Cloudflare (or you are willing to change your domain provider to Cloudflare)
  • You can run a docker container in your server
  • You have deno installed on your computer
  • You are comfortable writing some TypeScript code to check IP and update it using Cloudflare APIs

Get started

Cloudflare Domain Settings

Create an A record for your Dynamic DNS subdomain or full domain for your public IP.

You can skip this step, if you already have an A record for your public IP.

DNS record for your Public IP

Cloudflare API Token

Go to Account API Tokens > Create Token and select the Edit Zone DNS template.

API key permissions

You want to only allow Zone DNS Edit/Read permissions.

Scope API key to your zone

You also want to limit access to the specific zones you want to work with. In this case, you want to only work with the zone that will have your Dynamic DNS IP A Record. This will limit the blast radius of the API token.

Then you need to Continue to Summary and Create Token.

Store the API key securely while you build the rest.

Cloudflare Worker

You can either use the Cloudflare CLI or the Dashboard to create your worker. I started out by doing this on the Dashboard. Later, I generated a Cloudflare Worker project with CLI and pushed it to a Gitlab repo. Then I connected the existing Worker to the repo. This added support for automatic deployments on push to the main branch.

Create a hello world worker and get it deployed to Cloudflare workers. This will set up all the basics.

You might be asking, why are we using Cloudflare Workers? Because I preferred to keep the Cloudflare API related work within Cloudflare’s walled garden. API key, ideally, never leaves the platform. You will be directly making requests against the Cloudflare Worker and the worker will take care of interacting with Cloudflare API.

Architecture

Architecture for this setup

Code

We will be using TypeScript and Deno.

Cloudflare Worker

You need to make sure you have some secrets set:

  • WORKER_API_KEY: API key to authenticate with the Cloudflare worker
  • API_KEY: Cloudflare API key you created earlier
  • DNS_ZONE_ID: Cloudflare Zone ID. You can get it by making a GET request to https://api.cloudflare.com/client/v4/zones with "Authorization: Bearer API_KEY" in the headers. Be sure to replace API_KEY with your actual API key. Grab the id of the zone you want to update.
  • DNS_RECORD_ID: This is the ID of the Dynamic DNS record. You can find it by sending a GET request to https://api.cloudflare.com/client/v4/zones/DNS_ZONE_ID/dns_records. It will return an array of DNS records. Grab the id of the record you want to update.

wrangler.toml: These are the environment variables for the worker

[vars]
DDNS_NAME="ddns.YOUR_DOMAIN.com"
DDNS_COMMENT="DDNS for my server"
DDNS_PROXIED=true

index.ts

import { getIp } from "./get-ip";
import { updateDnsRecord } from "./update-dns-record";

export default {
  async fetch(request, env): Promise<Response> {
    const WORKER_API_KEY = env.WORKER_API_KEY
    const API_KEY = request.headers.get('Authorization')

    if (API_KEY !== WORKER_API_KEY) {
      return new Response('Not Authorized', { status: 403 })
    }

    if (request.method === 'PUT') {
      const { ip } = await request.json() as { ip: string }
      return updateDnsRecord(ip, env)
    } else {
      return getIp(env)
    }
  },
} satisfies ExportedHandler<Env>;

get-ip.ts

export async function getIp(env: Env) {
  const options = {
    method: "GET",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${env.API_KEY}`,
    }
  };

  try {
    const response = await fetch(
      `https://api.cloudflare.com/client/v4/zones/${env.DNS_ZONE_ID}/dns_records/${env.DNS_RECORD_ID}`,
      options
    )

    const { result: { content: ip } } = await response.json() as { result: { content: string } }

    return new Response(JSON.stringify({ ip }), { headers: { 'Content-Type': 'application/json' } });
  } catch (err) {
    console.error(err)
    return new Response(JSON.stringify(err))
  }
}

update-dns-record.ts

export async function updateDnsRecord(ip: string, env: Env) {
  if (ip) {
    // snake_case because Cloudflare API uses snake_case :)
    const zone_id = env.DNS_ZONE_ID

    const id = env.DNS_RECORD_ID
    const name = env.DDNS_NAME
    const comment = env.DDNS_COMMENT
    const proxied = env.DDNS_PROXIED

    try {
      const update = {
        id,
        zone_id,
        name,
        comment,
        type: "A",
        content: ip,
        proxied
      }
      const body = JSON.stringify(update);

      const options = {
        method: "PUT",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${env.API_KEY}`,
        },
        body,
      };

      const response = await fetch(
        `https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${update.id}`,
        options
      )

      const responseBody = await response.json()

      return new Response(JSON.stringify(responseBody), { headers: { 'Content-Type': 'application/json' } });
    } catch (err) {
      console.error(err)
      return new Response(JSON.stringify(err))
    }
  }

  return new Response(JSON.stringify({ message: 'bad request' }));
}

Docker Container

You can get started by installing deno on your machine and generate a new deno app with deno init ddns_sync.

I used Deno in this example, because it is so easy to get started and have a running code without installing bunch of stuff. Deno is so awesome for that. I installed zero packages to achieve cron and HTTP requests.

Dockerfile

FROM denoland/deno:alpine-2.1.4

WORKDIR /app

ADD *.ts .
ADD *.json .

RUN deno cache main.ts

ENTRYPOINT ["deno", "task", "start"]

main.ts

Deno.cron("Sample cron job", "*/1 * * * *", async () => {
  const currentDateTime = new Date().toISOString()
  try {
    const [publicIp, ddnsIp] = await Promise.all([
      getPublicIp(),
      getExistingDdnsIp(),
    ]);

    if (publicIp === ddnsIp) {
      console.log(currentDateTime, "IP address did not change");
    } else {
      const updatedIp = await updateDdnsIp(publicIp);
      console.log(
        currentDateTime,
        updatedIp === publicIp
          ? "DDNS IP has been updated"
          : "DDNS failed to update"
      );
    }
  } catch (error) {
    console.error(currentDateTime, 'Error: ', error)
  }
});

async function getPublicIp(): Promise<string> {
  const response = await fetch("https://api.ipify.org/?format=json");
  const body = await response.json();

  return body.ip;
}

async function getExistingDdnsIp(): Promise<string> {
  const response = await fetch(Deno.env.get("WORKER_URL"), {
    headers: { Authorization: Deno.env.get("API_KEY") },
  });
  const body = await response.json();

  return body.ip;
}

async function updateDdnsIp(ip: string) {
  const options = {
    method: "PUT",
    headers: {
      "Content-Type": "application/json",
      Authorization: Deno.env.get("API_KEY"),
    },
    body: JSON.stringify({ ip }),
  };

  const response = await fetch(Deno.env.get("WORKER_URL"), options);
  const body = await response.json();

  return body.result.content;
}

deno.json

{
  "tasks": {
    "dev": "deno run --unstable-cron --allow-net --allow-env --watch main.ts",
    "start": "deno run --unstable-cron --allow-env --allow-net main.ts"
  },
  "imports": {
    "@std/assert": "jsr:@std/assert@1"
  }
}

You can build your docker image and run it on your server like this:

docker build -t ddns-sync .
docker run -d --name ddns-sync \
  -e API_KEY=WORKER_API_KEY \
  -e WORKER_URL="http://ddns.YOUR_USERNAME.workers.dev" \
  --restart unless-stopped ddns-sync

That’s it. That should have you check your public IP every minute and compare it against the DNS record. If your public IP changes, this will automatically update it. You will get about a minute of outage if this happens.

I recently had to do this and decided to document it as a blog post. I hope someone finds this helpful!

The code quality is at the level of a homelab project, so there is definitely room for improvement. Let me know if you have suggestions on how to improve this.

If you have any questions or corrections, definitely leave a comment. I am more than happy to answer them.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Dynamic DNS sync with Cloudflare

Thematisch verwandte Begriffe: Dynamic, sync, with, Cloudflare · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94084 | Suricata before 8.0.7 has an Http2ThreadMultiBuf use-after-free when a t…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick