.env.example is the complete environment-variable reference. This guide
describes host behavior and the provider-specific values needed to get started.
Set UDDNS_PROVIDER to one of: cloudflare (default), duckdns, noip,
dynu, namecheap, dyndns, route53, porkbun, hetzner,
digitalocean, gandi, linode, ovh, bunny, contabo.
Update several names on one provider/account:
UDDNS_HOSTS=home.example.com,vpn.example.com,api.example.com
# Pause a host without removing it:
# UDDNS_DISABLED_HOSTS=vpn.example.comUDDNS_HOST remains available for one host. Each host is checkpointed
independently, so a partial failure retries only failed hosts. Checkpoints
default to .uddns-state.json; set UDDNS_STATE_FILE= to keep state in memory.
Disabled hosts are skipped and cannot be force-updated.
Multi-account YAML uses strict keys and provider subsections: misspelled or
unsupported keys fail validation instead of silently falling back to defaults.
Account ids must be 1–63 characters using letters, numbers, ., _, or -.
UDDNS_INTERVAL defaults to 900000 ms (15 minutes) and must be between
60000 and 86400000 ms (60 s–24 h). Absolute state/history paths can
be jailed under UDDNS_DATA_DIR when set.
UDDNS_IP_FAMILY selects dual (default), v4, or v6.
UDDNS_IP_MISSING=keep (default) reuses the previous address when discovery
fails for a family. clear omits that family from the next upsert so providers
do not rewrite it — it does not delete existing A/AAAA records at the DNS
provider.
HTTPS echo discovery uses pin-on-connect (resolve once, dial only verified
public addresses). DNS fallbacks stay off unless UDDNS_IP_DNS_FALLBACK=true.
See Security.
Transient transport errors, HTTP 429, and HTTP 5xx responses retry with exponential, jittered backoff. Defaults are three attempts, 1000 ms base delay, and 30_000 ms max delay. Override with:
# UDDNS_RETRY_ATTEMPTS=3
# UDDNS_RETRY_BASE_DELAY_MS=1000
# UDDNS_RETRY_MAX_DELAY_MS=30000When a provider response includes Retry-After, that delay is honored (capped
by the max retry delay).
When the same hostname can be updated at two DNS providers, configure a primary
account with an ordered failover list of standby account ids. Standby accounts
use role: failover and are validated with the rest of the file, but they do
not run their own updater loop.
version: 1
accounts:
- id: home-cf
provider: cloudflare
hosts: [home.example.com]
failover: [home-r53]
retry: { attempts: 3 }
cloudflare:
api_token: replace-me
zone_id: replace-me
- id: home-r53
role: failover
provider: route53
hosts: [home.example.com]
route53:
access_key_id: replace-me
secret_access_key: replace-me
hosted_zone_id: replace-meAfter the primary exhausts its retry budget (including non-retryable errors such
as HTTP 401), uDDNS tries each failover target that lists the same host. The
first success checkpoints on the primary state file. See
examples/uddns.failover.yaml.
Failover only helps when both providers can actually serve the FQDN (for example dual-provider DNS or a deliberate migration path).
Optional HTTPS notifications after cycles:
# UDDNS_NOTIFY_WEBHOOK_URL=https://example.com/hook
# UDDNS_NOTIFY_NTFY_URL=https://ntfy.sh/my-topic # LAN ntfy allowed
# UDDNS_NOTIFY_SLACK_URL=https://hooks.slack.com/services/...
# UDDNS_NOTIFY_DISCORD_URL=https://discord.com/api/webhooks/...
# UDDNS_NOTIFY_ON=change,errorWebhook/ntfy may target private LAN hosts; Slack/Discord may not. Loopback and cloud-metadata targets are always rejected.
Create an API token with Zone → DNS → Edit and add Zone → Zone → Read when resolving zones by name.
UDDNS_PROVIDER=cloudflare
UDDNS_INTERVAL=900000
UDDNS_HOSTS=home.example.com,vpn.example.com
CLOUDFLARE_API_TOKEN=your_api_token
# One of:
CLOUDFLARE_ZONE_ID=your_zone_id
# CLOUDFLARE_ZONE_NAME=example.com
# Optional:
# CLOUDFLARE_PROXIED=false
# CLOUDFLARE_TTL=1
# CLOUDFLARE_CREATE_IF_MISSING=true
# CLOUDFLARE_RECORD_ID=record_idWhen CLOUDFLARE_ZONE_ID is absent, uDDNS tries
CLOUDFLARE_ZONE_NAME, then walks parent domains for each host. Existing A/AAAA
records are patched. Missing records are created when
CLOUDFLARE_CREATE_IF_MISSING=true.
UDDNS_PROVIDER=duckdns
DUCKDNS_TOKEN=your_token
UDDNS_HOSTS=myhost,otherhost
# Or: DUCKDNS_DOMAINS=myhost,otherhostUDDNS_PROVIDER=noip
UDDNS_USER=your_username
UDDNS_PASS=your_password
UDDNS_HOSTS=myhost.ddns.net,other.ddns.netUDDNS_PROVIDER=dynu
UDDNS_USER=your_username
UDDNS_PASS=your_password
UDDNS_HOSTS=myhost.dynu.com,other.dynu.comUse the Dynamic DNS password, not the account password.
UDDNS_PROVIDER=namecheap
NAMECHEAP_DOMAIN=example.com
NAMECHEAP_PASSWORD=your_ddns_password
UDDNS_HOSTS=home,vpn,apiFQDN values such as home.example.com are also accepted in UDDNS_HOSTS.
Generic /nic/update clients, including Dyn.com-compatible services:
UDDNS_PROVIDER=dyndns
UDDNS_USER=your_username
UDDNS_PASS=your_password
UDDNS_HOSTS=myhost.example.com,other.example.com
# Optional; credentials require HTTPS and an allowlisted host:
# DYNDNS_UPDATE_URL=https://members.dyndns.org/nic/update
# Extra hosts beyond the built-in allowlist (comma-separated):
# DYNDNS_UPDATE_URL_ALLOW_HOSTS=ddns.example.netBuilt-in allowlist hosts: members.dyndns.org, members.dyndns.com,
update.dyndns.org, dynupdate.no-ip.com, dynupdate.no-ip.org. See
Security.
Use an IAM user (or key pair) limited to route53:ChangeResourceRecordSets
and route53:ListResourceRecordSets on the hosted zone. Records are
UPSERTed via the Route53 REST API with SigV4 signing — no AWS SDK required.
UDDNS_PROVIDER=route53
ROUTE53_ACCESS_KEY_ID=AKIA...
ROUTE53_SECRET_ACCESS_KEY=your_secret_key
ROUTE53_HOSTED_ZONE_ID=Z1234567890ABC
UDDNS_HOSTS=home.example.com,vpn.example.com
# Optional:
# ROUTE53_REGION=us-east-1
# ROUTE53_TTL=300
# ROUTE53_CREATE_IF_MISSING=trueCreate an API key pair under Account → API Access and enable API access for the domain.
UDDNS_PROVIDER=porkbun
PORKBUN_API_KEY=pk1_...
PORKBUN_SECRET_KEY=sk1_...
PORKBUN_DOMAIN=example.com
UDDNS_HOSTS=home,vpnFQDN values such as home.example.com are also accepted in UDDNS_HOSTS;
without PORKBUN_DOMAIN the registered domain is assumed to be the last two
labels of each host.
Create an API token in the Hetzner DNS console.
UDDNS_PROVIDER=hetzner
HETZNER_API_TOKEN=your_api_token
# One of (otherwise parent domains of each host are tried):
HETZNER_ZONE_ID=your_zone_id
# HETZNER_ZONE_NAME=example.com
UDDNS_HOSTS=home.example.com,vpn.example.comCreate a personal access token with domain read/write scope.
UDDNS_PROVIDER=digitalocean
DIGITALOCEAN_API_TOKEN=dop_v1_...
DIGITALOCEAN_DOMAIN=example.com
UDDNS_HOSTS=home.example.com,vpn.example.comWithout DIGITALOCEAN_DOMAIN the registered domain is assumed to be the last
two labels of each host.
Use a Personal Access Token with domain technical-configuration rights.
UDDNS_PROVIDER=gandi
GANDI_API_TOKEN=your_pat
GANDI_DOMAIN=example.com
UDDNS_HOSTS=home.example.com,vpn.example.com
# Optional: GANDI_TTL=300Create a personal access token with Domains read/write scope.
UDDNS_PROVIDER=linode
LINODE_API_TOKEN=your_token
LINODE_DOMAIN_ID=12345
LINODE_DOMAIN=example.com
UDDNS_HOSTS=home.example.com,vpn.example.com
# Optional: LINODE_TTL=300Create an application key, secret, and consumer key with DNS zone record
CRUD plus refresh rights. Endpoint is one of eu, ca, or us.
UDDNS_PROVIDER=ovh
OVH_ENDPOINT=eu
OVH_APPLICATION_KEY=...
OVH_APPLICATION_SECRET=...
OVH_CONSUMER_KEY=...
OVH_ZONE=example.com
UDDNS_HOSTS=home.example.com,vpn.example.com
# Optional: OVH_TTL=300Use the account API key from the bunny.net dashboard.
UDDNS_PROVIDER=bunny
BUNNY_API_KEY=your_access_key
BUNNY_ZONE_ID=12345
BUNNY_DOMAIN=example.com
UDDNS_HOSTS=home.example.com,vpn.example.com
# Optional: BUNNY_TTL=300Create OAuth client credentials and an API user in the Contabo control panel.
UDDNS_PROVIDER=contabo
CONTABO_CLIENT_ID=...
CONTABO_CLIENT_SECRET=...
CONTABO_API_USER=you@example.com
CONTABO_API_PASSWORD=...
CONTABO_ZONE=example.com
UDDNS_HOSTS=home.example.com,vpn.example.com
# Optional: CONTABO_TTL=300