Reference

Protocols DynIP speaks

DynIP accepts dynamic DNS updates over three protocols. Most clients are happy out of the box — the platform recognizes what each one expects and answers in kind. This page explains the three, which tools use each, and how to pick.

The three protocols at a glance

Protocol Transport Typical clients
dyndns2 HTTP(S) GET to /update inadyn, ddclient, consumer routers, UniFi / EdgeOS
RFC 2136 DNS UPDATE DNS over UDP/TCP port 53, TSIG-signed external-dns, cert-manager, lego, certbot, BIND nsupdate, Caddy, Traefik
Native JSON API HTTP(S) GET to /update scripts, the DynIP updater container, custom tooling

dyndns2 and the native JSON API hit the same /update endpoint — they differ only in how the response is formatted. RFC 2136 is a separate path entirely: native DNS UPDATE packets, TSIG-signed, sent to the nameservers on port 53.

dyndns2 (HTTP)

The dyndns2 convention is the de facto standard that DynDNS popularized and that most embedded DDNS clients speak. The client issues an HTTP GET with the new IP; the server answers HTTP 200 with a one-token plain-text body whose first word signals the outcome. Clients reject anything that isn't this shape — a JSON body comes back to them as an "Error response from DDNS server".

DynIP maps each outcome to the standard dyndns2 tokens:

BodyMeaning
good <ip>Update applied; the record changed to <ip>.
nochg <ip>Already current — the IP matched, nothing was written.
badauthThe TSIG key is wrong (sent as the password).
nohostNo such zone, or it isn't yours.
notfqdnMalformed request — e.g. an unparseable IP.
abuseRate limited (more than 60 updates/min from one source).
911Server-side failure applying the update; retry later.

Every one of these is returned with HTTP status 200 — that's the dyndns2 convention. The outcome lives in the body, not the status code.

How the platform picks dyndns2 vs JSON

The /update endpoint defaults to JSON. It switches to the dyndns2 plain-text format when either of the following is true:

  • The request sends Accept: text/plain (an explicit Accept: application/json always wins the other way), or
  • The User-Agent identifies a known dyndns2 client — the match list covers inadyn, ddclient, unifi, ubiquiti, and edgeos.

So inadyn and ddclient get dyndns2 automatically, with no special server flag and no client-side response-matching regex. If you're writing your own client and want dyndns2, set Accept: text/plain; if you want JSON, send Accept: application/json (or nothing — JSON is the default).

inadyn

Because the platform now returns dyndns2 for inadyn's User-Agent, the config is clean — no response-matching line is needed. inadyn 2.9.0 treats an HTTP 200 as success by default, which is exactly what DynIP returns on good/nochg:

# /etc/inadyn.conf
period          = 300
user-agent      = "Mozilla/5.0 inadyn/2.9.0"

custom dynip-provider {
    hostname    = "example.ddns.dynip.dev"
    username    = "unused"
    password    = "YOUR_TSIG_KEY"
    ddns-server = "api.dynip.dev"
    ddns-path   = "/update?domain=%h&ip=%i&key=%p"
}

Earlier guidance suggested a response = '"status":"success"' line to match the JSON body with a regex. Drop it — it's no longer needed now that the server speaks dyndns2 to inadyn. If you do want an explicit match, the dyndns2-native form is response = ['^good', '^nochg'], but inadyn's default HTTP-200 handling already covers both.

ddclient

ddclient speaks dyndns2 as a first-class protocol. The platform recognizes its ddclient/X.Y.Z User-Agent and answers in dyndns2 automatically:

# /etc/ddclient.conf
protocol=dyndns2
use=web, web=https://api.ipify.org
server=api.dynip.dev
ssl=yes
login=unused
password=YOUR_TSIG_KEY
example.ddns.dynip.dev

ddclient re-posts every cycle; the good / nochg distinction in the response lets it log whether the IP actually moved.

RFC 2136 DNS UPDATE (TSIG)

RFC 2136 is the IETF standard for dynamic updates to a DNS zone — not an HTTP convention but real DNS UPDATE packets over port 53, authenticated with a TSIG transaction signature. This is the protocol of choice for infrastructure tooling that already lives in the DNS world.

Updates go to update.dynip.dev:53, which resolves to all secondaries; each verifies the TSIG signature locally before applying the change. The key name is key-<your-zone> and the secret is your zone's Base64 TSIG key — the same secret the HTTP path uses, just presented as a DNS TSIG rather than a URL parameter.

Algorithm matters here. RFC 2136 clients pin a TSIG algorithm. DynIP defaults new zones to HMAC-SHA256, which external-dns, lego, certbot, and nsupdate all expect. FortiGate's genericDDNS is the exception — it requires HMAC-MD5. Set the algorithm per zone in the dashboard's Options modal before configuring the client.

Worked examples below use BIND's nsupdate, the canonical RFC 2136 client. Replace example.ddns.dynip.dev with your zone and YOUR_TSIG_KEY with the secret from your zone's Options modal. All examples send a multi-line heredoc — paste the entire block into your shell as-is.

To skip the substitutions, open your zone's Snippets in the dashboard and choose nsupdate (RFC 2136). Pick a record type, enter the name and content, and it renders the command below with your key, algorithm and zone already filled in.

A record (IPv4)

The default for dynamic DNS — most clients update A records exclusively. Short TTL (60s) lets the value change rapidly without resolvers caching stale data.

nsupdate -y 'hmac-sha256:key-example.ddns.dynip.dev:YOUR_TSIG_KEY' <<'EOF'
server update.dynip.dev
zone example.ddns.dynip.dev
update delete example.ddns.dynip.dev. A
update add    example.ddns.dynip.dev. 60 A 203.0.113.42
send
EOF

AAAA record (IPv6)

Identical syntax to A; just the record type changes. A and AAAA at the same name coexist — clients on dual-stack networks pick whichever family they support.

nsupdate -y 'hmac-sha256:key-example.ddns.dynip.dev:YOUR_TSIG_KEY' <<'EOF'
server update.dynip.dev
zone example.ddns.dynip.dev
update add example.ddns.dynip.dev. 60 AAAA 2001:db8::1
send
EOF

TXT record

Used for ACME DNS-01 challenges (_acme-challenge.<zone>), SPF, DKIM, DMARC, and domain ownership verification. Values must be quoted — the quotes are part of the wire format.

nsupdate -y 'hmac-sha256:key-example.ddns.dynip.dev:YOUR_TSIG_KEY' <<'EOF'
server update.dynip.dev
zone example.ddns.dynip.dev
update add _acme-challenge.example.ddns.dynip.dev. 60 TXT "challenge-value-from-acme-client"
send
EOF

CNAME record

Aliases one name to another. Useful for pointing service subdomains at external load balancers, CDN endpoints, or shared infrastructure.

Important: CNAMEs cannot exist at the zone apex (where SOA and NS records live). Use them only at subdomain labels (web.example.ddns.dynip.dev), never at the apex itself.
nsupdate -y 'hmac-sha256:key-example.ddns.dynip.dev:YOUR_TSIG_KEY' <<'EOF'
server update.dynip.dev
zone example.ddns.dynip.dev
update add web.example.ddns.dynip.dev. 3600 CNAME edge.example-cdn.com.
send
EOF

Note the trailing dot on edge.example-cdn.com. — it makes the name absolute. Without it, BIND treats the target as relative to your zone, which is almost never what you want.

MX record (mail exchanger)

Designates the mail server(s) for a domain. The priority value (a number, lower = higher preference) precedes the target host.

nsupdate -y 'hmac-sha256:key-example.ddns.dynip.dev:YOUR_TSIG_KEY' <<'EOF'
server update.dynip.dev
zone example.ddns.dynip.dev
update add example.ddns.dynip.dev. 3600 MX 10 mail.example.com.
send
EOF

Format: <priority> <target>. Multiple MX records at the same name are normal — add them in separate update add lines within one nsupdate session, then send.

SRV record (service discovery)

Used by services that advertise port + host (Matrix, XMPP, SIP, Minecraft, etc.). Four-component value: priority weight port target.

nsupdate -y 'hmac-sha256:key-example.ddns.dynip.dev:YOUR_TSIG_KEY' <<'EOF'
server update.dynip.dev
zone example.ddns.dynip.dev
update add _matrix._tcp.example.ddns.dynip.dev. 3600 SRV 10 5 8448 matrix.example.ddns.dynip.dev.
send
EOF

SRV names always use the _service._protocol.<zone> convention (e.g. _matrix._tcp, _sip._tls, _minecraft._tcp). The service and protocol prefixes are part of the spec, not optional.

Removing records

Replace update add with update delete. Include the value to remove just that specific value; omit it to remove all records of that type at the name.

# Remove a specific value
update delete web.example.ddns.dynip.dev. CNAME edge.example-cdn.com.

# Remove all A records at a name
update delete example.ddns.dynip.dev. A

# Remove every record at a name (rare; usually overkill)
update delete experimental.example.ddns.dynip.dev.

Combining multiple changes atomically

Multiple update lines in a single nsupdate session are sent as one atomic UPDATE message — either all succeed or none do. This avoids any window where the name resolves to nothing during a rotation.

nsupdate -y 'hmac-sha256:key-example.ddns.dynip.dev:YOUR_TSIG_KEY' <<'EOF'
server update.dynip.dev
zone example.ddns.dynip.dev
update delete example.ddns.dynip.dev. A
update add    example.ddns.dynip.dev. 60 A 198.51.100.42
send
EOF

Verifying the change

After nsupdate returns silently (its convention for success), query an authoritative nameserver directly to bypass any DNS caches:

dig +short A    example.ddns.dynip.dev @ns1.dynip.dev
dig +short TXT  _acme-challenge.example.ddns.dynip.dev @ns1.dynip.dev
dig +short MX   example.ddns.dynip.dev @ns1.dynip.dev

Typical propagation between primary and secondaries is <5 seconds. If you query @ns1.dynip.dev and the record isn't there yet, give it 10-30 seconds (NOTIFY + AXFR cycle), then retry.

Tools that drive this protocol for you:

  • external-dns — publishes records from Kubernetes Service and Ingress annotations. See the external-dns guide.
  • cert-manager — Kubernetes-native certificate management, gets Let's Encrypt certs for your services automatically via the DNS-01 challenge over RFC 2136. See the cert-manager guide.
  • lego and certbot — solve the ACME DNS-01 challenge by writing _acme-challenge TXT records over RFC 2136 (the rfc2136 provider) to get Let's Encrypt certificates without exposing port 80.
  • Caddy and Traefik — same DNS-01 pattern via their RFC 2136 DNS modules; see the Caddy and Traefik guides.
  • FortiGate — native genericDDNS over TSIG (HMAC-MD5); see the FortiGate guide.

Newly created zones take a moment to propagate the TSIG key to the secondaries before RFC 2136 updates succeed. The dashboard surfaces this as a "propagating" state on the zone; the HTTP path works immediately because it talks to the primary.

Native JSON API

This is the default response format of the /update endpoint and the friendliest one for scripts. Same URL as dyndns2 — the difference is the body. With no dyndns2 User-Agent and no Accept: text/plain, /update answers with JSON:

curl "https://api.dynip.dev/update?domain=example.ddns.dynip.dev&ip=203.0.113.42&key=YOUR_TSIG_KEY"
# {"status": "success", "type": "A", "ip": "203.0.113.42"}

The record type (A vs AAAA) is inferred from the IP you send. All three parameters are required — a device that doesn't know its own public IP discovers it first with a call to /ip (or /ip4 / /ip6 for a specific family), then passes the result to /update. Error cases come back as a non-200 status with a JSON detail body, the conventional REST shape.

The endpoint also accepts POST with the same three fields as a JSON body:

curl -X POST https://api.dynip.dev/update \
     -H "Content-Type: application/json" \
     -d '{"domain": "example.ddns.dynip.dev", "ip": "203.0.113.42", "key": "YOUR_TSIG_KEY"}'
# {"status": "success", "type": "A", "ip": "203.0.113.42"}

Beyond /update, the native API also covers IP discovery (/ip, /ip4, /ip6) and the full account-scoped Zone Management API (register, list, delete, DNSSEC, SSL, BYOD) authenticated with a JWT bearer token. The API documentation has the complete reference.

The DynIP updater container uses this JSON path: it reads the current IP from /ip4 / /ip6, compares, and only calls /update when the address actually changes.

Which should I use?

  • A router, firewall, or NAS with a built-in DDNS client? Use dyndns2 — pick a "custom" provider and point it at api.dynip.dev/update. It Just Works for inadyn, ddclient, UniFi, and most consumer gear.
  • Kubernetes, ACME / Let's Encrypt, or anything DNS-native? Use RFC 2136. It's the standard those ecosystems already speak, and it carries TSIG end to end.
  • Writing a script or your own tooling? Use the native JSON API — predictable JSON, IP auto-detection, and the same endpoint your dashboard uses.

The dashboard's Snippets generator produces a ready-to-paste config for each of these — choose your device and it emits the right protocol and response handling automatically.

What's next