cfdyn puts a web interface in front of favonia/cloudflare-ddns, the excellent dynamic-DNS updater configured entirely through environment variables. Pick hostnames from your actual Cloudflare zones, set the schedule, and let cfdyn install and run the updater for you — no YAML editing, no reading a variable reference, no docker run incantations.
Features
- Five-step setup wizard — password, token, hostnames, schedule, done. The token is verified against Cloudflare before anything saves, and hostnames are picked from your real zones instead of typed from memory
- Live overview — the address your network currently presents, whether the updater is running, and a minute-by-minute pulse rail showing whether your DNS records actually agree with reality — cfdyn's own measurements against the Cloudflare API, not text scraped from logs
- Hostname management — each hostname's live DNS record shown beside the detected address, per-hostname proxy toggles, and one-click adoption of records that already exist in your zones
- Every upstream option, explained — grouped plain-language settings with the rarely needed ones behind an advanced toggle; the generated-configuration preview doubles as a standalone compose file for running the updater without cfdyn
- One-click upstream updates — the updater is favonia's published image, run unmodified; their fixes reach you the moment you press Install update (or automatically, if you opt in)
- Container & logs — start, stop, restart, or recreate the updater and tail its log live, all from the console
Install with Docker Compose
Requirements: a Linux host with Docker and the compose plugin, and a Cloudflare account with at least one zone.
mkdir cfdyn && cd cfdyn
curl -fsSLO https://raw.githubusercontent.com/hyprlab/cfdyn/main/docker-compose.yml
docker compose up -d
Then open http://your-server-ip:8787 and follow the wizard. That's the whole install — cfdyn pulls and configures the DDNS updater itself during setup. Only one service is declared in the compose file: the cloudflare-ddns container is created and managed by cfdyn at runtime, straight from the upstream published image.
The wizard walks five steps, and nothing touches Cloudflare until the final one:
- Password — the console locks itself first: scrypt-hashed, with a lockout after repeated failures
- Token — paste a Cloudflare API token scoped to Zone → DNS → Edit for just the zones you manage; cfdyn verifies it immediately and stores it encrypted
- Hostnames — pick from the A/AAAA records already in your zones, or type new ones; the updater creates records that don't exist yet
- Schedule — check interval (default every 5 minutes), record TTL, and IP detection
- Review — see the exact container configuration, then Install and start
Configuration
Everything about DNS behaviour — schedule, TTL, proxying, IP detection, notifications (Healthchecks.io, Uptime Kuma, shoutrrr), WAF lists — is configured in the web UI under Settings. A .env file next to the compose file covers the rest, and everything is optional:
| Variable | Default | Purpose |
|---|---|---|
CFDYN_PORT | 8787 | Port the console listens on |
CFDYN_IMAGE_TAG | 1 | cfdyn image tag to run (1 tracks the stable 1.x line) |
CFDYN_DDNS_IMAGE | favonia/cloudflare-ddns | Upstream updater image |
CFDYN_DDNS_TAG | 1 | Updater tag to track — 1 is upstream's stable v1.x line |
CFDYN_DDNS_CONTAINER_NAME | cloudflare-ddns | Name of the managed updater container |
CFDYN_DDNS_USER | 1000:1000 | UID:GID the updater drops to |
TZ | UTC | Timezone for the console and cron schedules |
How updates work
Two separate things update independently, by design. The updater is never forked, vendored, patched, or rebuilt — cfdyn pulls the published favonia/cloudflare-ddns:1 image and runs it as a sibling container with the hardening upstream recommends (read_only, cap_drop: all, no-new-privileges, non-root), so an upstream release needs nothing from cfdyn to reach you. cfdyn itself updates the standard way:
docker compose pull && docker compose up -d
Your password, token, hostnames, and settings live in the cfdyn-data volume and survive updates. The updater keeps running — and keeps updating DNS — even when the console is down.
Security notes
- The Docker socket is root-equivalent — managing a sibling container requires mounting it, the same trust you extend to Watchtower or Portainer
- The API token is handled carefully — encrypted at rest and delivered to the updater as a file in a private volume, so it never appears in
docker inspect - The console is password-locked from first run, with scrypt hashing and a login lockout
- Keep it off the public internet — there is no TLS and no multi-user model; LAN or VPN only, and set
CFDYN_HTTPS=truebehind an HTTPS reverse proxy
cfdyn's own Cloudflare access is read-only — verifying tokens, listing zones and records, and comparing them against the detected address. All record writing is done by the upstream updater.
AI notice
cfdyn is built by a human maintainer working with generative AI as a development tool:
- Code — the large majority of the Python code in the repository was written with Anthropic's Claude (via Claude Code), working from the maintainer's direction. The maintainer decides what gets built, reviews the results, tests every release, and signs off on everything that ships.
- Text — documentation, release notes, and in-app copy are largely AI-drafted and human-edited.
- The app itself contains no AI. cfdyn has no AI features and makes no requests to AI services — it only drives the upstream cloudflare-ddns updater, and your Cloudflare tokens and DNS records go nowhere else. AI was used to build the app, not to run it.
Bug reports and pull requests are welcome from humans and their AI tools alike; everything merged gets the same human review.
Tech stack & license
Flask · SQLite · the Docker SDK — no frontend framework, no CDNs, no build step. Free and open source under the GNU AGPL-3.0. cfdyn is an independent companion to favonia/cloudflare-ddns (Apache 2.0, used as published) and is not affiliated with or endorsed by it — all credit for the actual DNS updating belongs upstream. Full docs and releases on GitHub, images on Docker Hub.