cloudflare-ddns v2.3.0 — Multi-interface support, reliability fixes & docs overhaul
⚠️ Upgrade notes
- Invalid settings now stop the container at startup. Values that used to
be ignored or silently replaced by a default (regexes,PROXIED,
WAF_LISTS,TTL, timeouts, malformed API tokens) now produce an explicit
error. Check the startup log after upgrading. - Docker Compose file names swapped.
docker/docker-compose.ymlis now
environment-variable mode; the legacyconfig.jsonexample is
docker/docker-compose.legacy.yml. - Helm chart 0.2.1. Notification and heartbeat URLs are stored in the
Secret, and the pod runs with a restricted security context.
New features
- Interface-bound providers (#248).
cloudflare.trace,cloudflare.doh,
ipifyandurl:accept an@<interface>suffix
(IP4_PROVIDER=cloudflare.trace@ens5) to send detection through a specific
network interface. - Detection groups (#248).
DOMAINS_N,IP4_DOMAINS_N,IP6_DOMAINS_N,
IP4_PROVIDER_NandIP6_PROVIDER_N(N ≥ 2) give groups of domains their
own providers. See docs/multi-interface.md.
Behavior changes
- Graceful shutdown on SIGTERM as well as SIGINT.
docker stop,
Kubernetes pod termination and systemd now trigger the same shutdown path
as Ctrl+C:DELETE_ON_STOPcleanup runs and the exit heartbeat is sent.
Waits between updates are interruptible, so shutdown is immediate. - Failed Cloudflare writes are reported as failures. A failed create,
update or delete now sends a fail heartbeat and a notification instead of
being logged and treated as success. - Invalid settings are startup errors. An invalid
MANAGED_RECORDS_COMMENT_REGEX,MANAGED_WAF_LIST_ITEMS_COMMENT_REGEX,
PROXIEDexpression,WAF_LISTSentry,TTL,DETECTION_TIMEOUTor
UPDATE_TIMEOUTnow stops the process with an error instead of silently
falling back to a default (or silently dropping the entry). UPDATE_CRONminimum interval is 30s. Lower@everyvalues are
clamped to 30s with a warning.- Legacy
--repeatinterval. In legacyconfig.jsonmode the polling
interval is the TTL, but never less than 300s when the TTL is 1 (auto);
anyttlbelow 30 now polls every 300s instead of every second. - Exit code. One-shot runs (
UPDATE_CRON=@once, or legacy mode without
--repeat) exit with code 1 if the update failed. - Uptime Kuma URL (#302). A query string on
UPTIMEKUMA(such as the
?status=up&msg=OK&ping=that Uptime Kuma shows) is stripped
automatically, so both the bare push URL and the copied URL work. - API token validation (#292). Surrounding quotes on the token are stripped
with a warning; a token containing whitespace or other invalid characters
is a clear startup error (the token itself is never printed). In legacy
mode an empty or placeholder token is a startup error.
Cloudflare's "Invalid format for Authorization header" error now comes with
a hint that the value is likely the Global API Key or a token ID. - WAF lists are created if missing. A list named in
WAF_LISTSthat does
not exist is created, usingWAF_LIST_DESCRIPTIONas its description
(requires the Account Filter Lists Edit permission). - Telegram messages are sent as plain text (no Markdown parse mode), so
domain names with_or*no longer break delivery. - Logging. Heartbeat and notification failures are logged with the HTTP
status; secrets in notification URLs are redacted in logs.
Fixes
- DNS records are looked up by name server-side. Zones with more than 100
A/AAAA records could miss the target record and try to recreate it every
cycle. - A failed record listing is treated as a failure rather than "no records",
which could create duplicates. - Legacy mode: failed writes are reported, record names are matched
case-insensitively, andpurgeUnknownRecordson definitive IP absence now
deletes only the configured subdomains' records instead of every A/AAAA
record in the zone. cloudflare.doh,ipifyandurl:providers are pinned to the requested
address family (previously onlycloudflare.tracewas), fixing detection
failures on dual-stack hosts.- Multicast, reserved (240/4), benchmarking (198.18/15),
0.0.0.0/8and
IPv4-mapped IPv6 addresses are no longer accepted as detected IPs. - WAF list items are read with cursor pagination; large lists were truncated.
DELETE_ON_STOPreports per-domain failures in the exit heartbeat and
notification (previously it always claimed success and sent two exit
heartbeats).- Zone IDs are cached instead of looked up on every cycle.
Documentation and deployment
- README restructured: quick start with exact token permissions, how-it-works
section, configuration by topic, one canonical environment variable
reference, deployment, security notes and troubleshooting. - Legacy
config.jsonmode documented separately in
docs/legacy-config.md, including a migration
table to environment variables. - Corrected documentation: the IPv4 default provider is
cloudflare.trace
(#294); Pushover URL ispushover://shoutrrr:api-token@user-key;
scheduling supports@every/@onceonly (not cron expressions). docker/docker-compose.ymlis now environment-variable mode; the legacy
file isdocker/docker-compose.legacy.yml. Obsoleteversion:and
PUID/PGIDremoved.- systemd unit: absolute
ExecStart, optional
EnvironmentFile=/etc/cloudflare-ddns/envfor environment-variable mode,
and sandboxing (DynamicUser,ProtectSystem=strict, ...). - Helm chart 0.2.1 (appVersion 2.3.0): notification and heartbeat URLs moved
into the Secret,existingSecretcan carry them, newdeleteOnFailure,
rejectCloudflareIps,wafListDescription,wafListItemCommentand
managedWafListItemsCommentRegexvalues, restricted security context and
Recreatestrategy. k8s/cloudflare-ddns.ymlrewritten for environment-variable mode (legacy
manifest kept ask8s/cloudflare-ddns.legacy.yml).- Legacy
config.jsonnow supportsrecordComment(#289). - Release notes consolidated into this changelog.
Docker images
timothyjmiller/cloudflare-ddns:2.3.0
timothyjmiller/cloudflare-ddns:2.3
timothyjmiller/cloudflare-ddns:2
timothyjmiller/cloudflare-ddns:latest
Helm chart: oci://ghcr.io/timothymiller/cloudflare-ddns version 0.2.1.
Full changelog: v2.2.0...v2.3.0