Small Cloudflare DDNS updater with a status page. Python standard library only, runs non-root.
13
A small Cloudflare dynamic DNS updater with a one page status UI. It keeps one or more A records pointed at the public IPv4 address your network appears from.
It is built for networks where the gateway cannot tell its own public address, for example a router sitting in a DMZ behind an ISP modem. Rather than reading a WAN interface, it asks the outside world which address it is seen from.
/cdn-cgi/trace, falling back to api4.ipify.org.DDNS_DOMAINS it creates or updates the A record through the Cloudflare API when the address differs.managed by cntr-ddns-01./config/state.json, so history survives a redeploy.python:3.12-slim, Python standard library only, no pip packages.no-new-privileges.7591. Health check on /healthz.services:
ddns:
image: 3nderium/labddns:latest
restart: unless-stopped
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
environment:
CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}
DDNS_DOMAINS: vpn.example.com,home.example.com
DDNS_USER: ${DDNS_USER}
DDNS_PASSWORD: ${DDNS_PASSWORD}
ports:
- "7591:7591"
volumes:
- ddns-config:/config
volumes:
ddns-config:
Then open http://<host>:7591.
The Cloudflare API token needs Zone > DNS > Edit on the zone that holds the names.
| Variable | Default | Purpose |
|---|---|---|
CLOUDFLARE_API_TOKEN | none, required | API token with Zone > DNS > Edit on the zone |
DDNS_DOMAINS | none useful, set it | Comma separated names to keep updated |
DDNS_INTERVAL_SECONDS | 300 | Seconds between checks, minimum 60 |
DDNS_TTL | 60 | TTL for records it creates or updates |
DDNS_USER, DDNS_PASSWORD | unset | Login for the web UI. With both unset the UI is open |
DDNS_SECRET | generated | Cookie signing secret. Generated into /config when unset |
TRUSTED_PROXY | unset | IP of your reverse proxy. Only its X-Forwarded-For is believed |
LAB_DOMAIN | unset | Your domain. Used with DDNS_HTTPS_ONLY |
DDNS_HTTPS_ONLY | false | true sends any request not from TRUSTED_PROXY to https://ddns.<LAB_DOMAIN> and marks the cookie Secure |
DDNS_STATUS_CLIENTS | unset | Comma separated IPs allowed to GET /api/state without a login, for a dashboard |
PORT | 7591 | Listen port |
TZ | UTC | Time zone for log lines |
Always set DDNS_DOMAINS to your own names.
| Path | Purpose |
|---|---|
/ | Status page |
/healthz | Health check, no login |
/api/state | Current state as JSON |
/api/check | POST to run a check now |
/config holds state.json (IP history and per record results) and the generated cookie secret.
latest only. Built by hand for a home lab, so there is no release schedule and no support. Use it as is or as a starting point for your own.
Content type
Image
Digest
sha256:a905a7f30…
Size
41.3 MB
Last updated
about 10 hours ago
docker pull 3nderium/labddns