A lightweight, self-contained Dynamic DNS client for Cloudflare written in Go.
It automatically detects your public IPv4 address and updates a Cloudflare DNS A record, ideal for home servers, dynamic IP connections, or lightweight edge nodes.
For a detailed breakdown of how it works, check out the full blog post.
This tool periodically:
- Fetches your current public IPv4 using a configurable API (e.g.
https://api.ipify.org). - Compares it with the A record on Cloudflare.
- Creates or updates the DNS record if the IP has changed.
It’s designed to:
- Run as a simple daemon or systemd service.
- Use a specific network interface for outbound requests (supports
SO_BINDTODEVICE). - Require no external dependencies — just Go and a Cloudflare API token.
One liner:
curl -fsSL https://raw.githubusercontent.com/ab-a/cf-ddns/main/install.sh | sudo bash -s -- \
--arch amd64 --iface eth1 --token <TOKEN> --zone example.com --record ddns.example.comAuto-detect architecture (recommended):
curl -fsSL https://raw.githubusercontent.com/ab-a/cf-ddns/main/install.sh
sudo bash install.sh \
--iface eth1 \
--token <CLOUDFLARE_TOKEN> \
--zone example.com \
--record ddns.example.comForce architecture:
curl -fsSL https://raw.githubusercontent.com/ab-a/cf-ddns/main/install.sh
sudo bash install.sh --arch amd64 --iface eth1 --token <TOKEN> --zone example.com --record ddns.example.com
sudo bash install.sh --arch arm64 --iface eth1 --token <TOKEN> --zone example.com --record ddns.example.comSkip checksum (not recommended):
curl -fsSL https://raw.githubusercontent.com/ab-a/cf-ddns/main/install.sh
sudo bash install.sh --no-check --iface wlp4s0 --token <TOKEN> --zone example.com --record ddns.example.comAll settings are defined in a YAML file (default: /etc/cf-ddns/config.yaml).
# Network interface to use for outbound requests (Linux only)
interface: "eth2"
# Public IP API — must return the IPv4 in plain text
ip_api: "https://api.ipify.org"
# How often to refresh the DNS record
interval: "5m"
cloudflare:
# Cloudflare API token with "Zone:Read" and "DNS:Edit" permissions
api_token: "your-api-token-here"
# Your Cloudflare zone and record names
zone_name: "example.com"
record_name: "ddns.example.com"
# TTL (1 = auto)
ttl: 1
# Whether to enable Cloudflare proxy (orange cloud)
proxied: falseYou can override the default config path with the environment variable:
export CF_DDNS_CONFIG=/path/to/config.yaml git clone https://github.com/ab-a/cf-ddns.git
cd cf-ddns
go get gopkg.in/yaml.v3
go build -o cf-ddns .
sha256sum cf-ddns > cf-ddns.sha256
sha256sum -c cf-ddns.sha256
go build -o cf-ddns .sudo ./cf-ddnsRequires root or CAP_NET_RAW privileges when using SO_BINDTODEVICE (binding to a network interface).
If you don’t need interface binding, you can simplify the code by removing that part.
You can set it up as a service for continuous operation:
[Unit]
Description=Cloudflare DDNS updater (interface-bound)
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/cf-ddns
Environment=CF_DDNS_CONFIG=/etc/cf-ddns/config.yaml
# Needs root to use SO_BINDTODEVICE; you can also add AmbientCapabilities if you want to drop full root.
User=root
Group=root
# Hardening (adjust if needed)
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
CapabilityBoundingSet=CAP_NET_RAW CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_RAW
ReadWritePaths=/etc/cf-ddns
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable cf-ddns
sudo systemctl start cf-ddns- Timeouts: Default HTTP timeout is 10 seconds.
- API compatibility: Works with any Cloudflare zone and a valid A record.
- Safety: Only writes to Cloudflare when a change is detected.
- Platform: Tested on Linux; other OSes may need adaptation since it uses
SO_BINDTODEVICE.
ddns.example.com set to 11.22.33.44
On subsequent runs, if the IP hasn’t changed, it will silently skip the update.
Store your Cloudflare API token securely. The token should have:
Zone:ReadDNS:Edit
permissions, scoped only to the zone you’re updating.
| Issue | Possible Cause | Fix |
|---|---|---|
permission denied on SO_BINDTODEVICE |
Insufficient privileges | Run as root or set CAP_NET_RAW |
non-2xx from IP API |
IP service unreachable | Try another API endpoint |
zone lookup failed |
Wrong zone_name or invalid token |
Double-check your Cloudflare credentials |
Cloudflare write failed |
Invalid record config | Ensure the record exists or allow creation |
MIT — free to use, modify, and share.
Credit is appreciated if you build on it.