#upnp #cloudflare #ddns #dns #dns-records #ip-address

app ddns-update-daemon

DynDNS update daemon using UPnP

1 stable release

1.0.0 May 5, 2024

#1043 in Network programming

MIT license

787 lines

DynDNS Update Daemon

A dynamic DNS (ddns) daemon that uses Universal Plug and Play (UPnP) to periodically check the internet gateway's external IP address, and if it has changed push an update to a ddns service or run a program.

If router_ip is set to the loopback address, the ddns-update-daemon will watch the local IP instead of the remote IP. In this case, no UPnP queries are sent.

Runs on Linux, Windows and Mac.
(Uses rustls on Linux and the OS's default everywhere else).


  • Watching the external IP address with UPnP.
    • Note: UPnP port-forwarding doesn't need to be enabled for this.
  • Watching the local IP address.
  • Updaters
    • Cloudflare by using the cloudflare API to update DNS records.
    • Requests to custom URLs (e.g. for dynu, no-ip, etc.).
    • Running a program.
  • IPv6
    • Mostly only supported on FRITZ!Box routers.


Install a prebuilt executable from the current release.

Or, build from source and install with:

cargo install ddns-update-daemon

(Requires Rust to be installed).


ddns-update-daemon requires a configuration toml file as the first CLI argument. (Run ddns-update-daemon --help to get help information about the CLI).

The configuration format is the following:

# The check interval in minutes; may be fractional (i.e. `0.5`). Required.
interval = 30
# The IP address of the internet gateway device the be queried. Optional.
# If not specified, the first discovered internet gateway device will be used.
# If set to loopback (, the local IP address will be watched
# by querying the OS of the current local IP address.
router_ip = ""

# Cloudflare DNS records to update. Optional.
# The cloudflare API access token.
api_token = "<token>"
# The cloudflare zone ID of the site to be updated.
zone_id = "<zone ID>"

# One or more records to be updated. Optional.
# The name of the DNS record. Required.
name = "@"
# The type of the DNS record. Required.
# Supportes "A" or "AAAA" records.
type = "A" # or "AAAA"

# Update a DynDNS service with a request to a URL. Optional.
# The method used for the update request, supports "get", "post", "put", "patch", ...
# Optional, defaults to "get".
method = "get"

# The url to send the update request to, placeholders `{ipv4}` and `{ipv6}`
# will get replaced by the IPv4 and IPv6 address that was detected.
# Required.
url = "https://api.yourservice.com/nic/update?myip={ipv4}&myipv6={ipv6}"

# Additional headers to send, placeholders `{ipv4}` and `{ipv6}` will be replaced
# with the detected IPs in the header values.
# Optional.
headers = {"name" = "value", ...}

# The body of the request, if the request supports a body.
# Placeholders `{ipv4}` and `{ipv6}` will again be replaced by the detected IPs.
# Optional.
body = ""

# Run one or more programs when new IP(s) are detected. Optional.
# The program to run and command line arguments,
# `{ipv4}` and `{ipv6}` will be replaced with the detected IPs in the arguments.
# Required.
cmd = ["myprog", "{ipv4}"]

License: MIT


~469K SLoC