Intelligent DDNS updater that finds the fastest Cloudflare IP and updates DNS records automatically
1.8K
An intelligent DDNS update tool powered by CloudflareSpeedTest.
Current image release: 2.0.0 (deepbluethought/cloudflarespeedtestddns:2.0.0)
403/1034, 404, 5xx, and timeout responses are all rejected[NEW] and [KEEP] labels.env filehost_ip_max defaults to 1, preventing clients and DNS caches from randomly selecting a worse IPhost_ip_max only when DNS round-robin is intentional; every retained IP must pass the application probeVersion Configuration + Fallback Mechanism:
Cloudflare IP List Auto-Update:
Create docker-compose.yml file
services:
cloudflare-speedtest-ddns:
image: deepbluethought/cloudflarespeedtestddns:latest
container_name: cloudflare-ddns
environment:
- CLOUDFLARE_ST_VERSION=v2.3.4
- zone_id=${zone_id}
- api_token=${api_token}
- host_name=${host_name}
- host_ip_max=${host_ip_max}
- ws_probe_enabled=${ws_probe_enabled}
- ws_probe_host=${ws_probe_host}
- ws_probe_path=${ws_probe_path}
- speedtest_para=${speedtest_para}
- cron=${cron}
- healthcheck_cron=${healthcheck_cron}
restart: unless-stopped
Create .env file in the same directory
zone_id=your_cloudflare_zone_id
api_token=your_cloudflare_api_token
host_name=testip.yourdomain.com
host_ip_max=1
ws_probe_enabled=true
ws_probe_host=www.yourdomain.com
ws_probe_path=/your-websocket-path
ws_probe_tls_verify=true
ws_probe_candidate_limit=20
speedtest_para=-n 1000 -dn 2 -sl 5 -tl 100 -url https://download.parallels.com/desktop/v18/18.1.1-53328/ParallelsDesktop-18.1.1-53328.dmg
cron=0 * * * *
healthcheck_cron=15,45 * * * *
Start the container
docker compose up -d
View logs
docker compose logs -f
Manually execute a test (optional)
docker compose exec cloudflare-speedtest-ddns bash -c "cd /app && bash main.sh"
Note: If you want to build from source instead, clone the repository first:
git clone https://github.com/DeepBlueThought/Cloudflare-SpeedTest-DDNS.git cd Cloudflare-SpeedTest-DDNS # Then modify docker-compose.yml to use "build: ." instead of "image:"
docker run -d \
--name cloudflare-ddns \
--restart unless-stopped \
--env-file .env \
deepbluethought/cloudflarespeedtestddns:latest
docker run -d \
--name cloudflare-ddns \
`# Container name` \
\
--restart unless-stopped \
`# Restart policy: always restart unless manually stopped` \
\
-e CLOUDFLARE_ST_VERSION=v2.3.4 \
`# CloudflareSpeedTest version (optional, default v2.3.4)` \
`# Fallback to pre-installed version if download fails` \
\
-e zone_id="your_cloudflare_zone_id" \
`# Cloudflare Zone ID` \
`# How to get: Cloudflare → Select domain → Zone ID in right sidebar` \
\
-e api_token="your_cloudflare_api_token" \
`# Cloudflare API Token` \
`# How to get: Cloudflare → My Profile → API Tokens → Create Token` \
`# Required permission: Zone.DNS (Edit)` \
\
-e host_name="testip.yourdomain.com" \
`# Domain to update (supports subdomains)` \
\
-e host_ip_max=1 \
`# One application-valid IP avoids random DNS selection` \
\
-e ws_probe_enabled=true \
-e ws_probe_host="www.yourdomain.com" \
-e ws_probe_path="/your-websocket-path" \
`# Real TLS SNI/HTTP Host and WebSocket path` \
\
-e speedtest_para="-n 1000 -dn 2 -sl 5 -tl 100 -url https://download.parallels.com/desktop/v18/18.1.1-53328/ParallelsDesktop-18.1.1-53328.dmg" \
`# CloudflareSpeedTest parameters:` \
`# -n 1000 : Latency test threads (max 1000, higher for better performance)` \
`# -dn 2 : Download test count (stops after finding 2 qualifying IPs)` \
`# -sl 5 : Minimum speed threshold 5 MB/s (dynamically adjusted)` \
`# -tl 100 : Maximum latency threshold 100 ms (dynamically adjusted)` \
`# -url : Speed test file URL (recommend large file via Cloudflare CDN)` \
\
-e cron="0 * * * *" \
`# Linux Cron expression for scheduling tests` \
`# Examples:` \
`# "0 * * * *" Run every hour` \
`# "*/30 * * * *" Run every 30 minutes` \
`# "0 */6 * * *" Run every 6 hours` \
\
-e healthcheck_cron="15,45 * * * *" \
`# Lightweight current-IP check every 30 minutes; reselect on failure` \
\
deepbluethought/cloudflarespeedtestddns:latest
docker run --rm \
--env-file .env \
deepbluethought/cloudflarespeedtestddns:latest \
bash -c "cd /app && bash main.sh"
Complete docker-compose.yml example:
services:
cloudflare-speedtest-ddns:
build: .
container_name: cloudflare-ddns
environment:
# CloudflareSpeedTest version (optional, default v2.3.4)
- CLOUDFLARE_ST_VERSION=v2.3.4
# Cloudflare API Configuration
- zone_id=${zone_id}
- api_token=${api_token}
# Domain Configuration
- host_name=${host_name}
- host_ip_max=${host_ip_max}
# Real WebSocket application configuration
- ws_probe_enabled=${ws_probe_enabled}
- ws_probe_host=${ws_probe_host}
- ws_probe_path=${ws_probe_path}
- ws_probe_tls_verify=${ws_probe_tls_verify}
- ws_probe_candidate_limit=${ws_probe_candidate_limit}
# CloudflareSpeedTest Parameters (read from .env)
- speedtest_para=${speedtest_para}
# Linux Cron Expression
# Example: "0 * * * *" runs every hour
# Example: "*/30 * * * *" runs every 30 minutes
- cron=${cron}
- healthcheck_cron=${healthcheck_cron}
- log_file=${log_file}
restart: unless-stopped
| Variable | Required | Default | Description |
|---|---|---|---|
CLOUDFLARE_ST_VERSION | No | v2.3.4 | CloudflareSpeedTest version (fallback on failure) |
host_name | Yes | - | Domain name to update. Supports multiple domain names separated by commas or spaces (e.g., cf1.example.com,cf2.example.com). |
host_ip_max | No | 1 | Number of DNS A records to retain; single-IP mode is recommended |
zone_id | Yes | - | Cloudflare Zone ID, obtain from Cloudflare dashboard |
api_token | Yes | - | Cloudflare API Token, obtain from Cloudflare dashboard |
speedtest_para | Yes | - | Test parameters, see details below |
cron | Yes | - | Cron expression for scheduling |
ws_probe_enabled | No | auto | auto enables the gate when ws_probe_host is set; may be explicitly true or false |
ws_probe_host | With probe | - | Real TLS SNI and HTTP Host, such as www.example.com; no scheme, port, or path |
ws_probe_path | No | / | WebSocket request path, such as /deepblue |
ws_probe_tls_verify | No | true | Verify the certificate chain and hostname; disabling it is not recommended |
ws_probe_candidate_limit | No | 20 | Number of lowest-latency candidates that receive the full application probe |
ws_probe_tcp_timeout / ws_probe_tls_timeout / ws_probe_http_timeout | No | 3 / 5 / 8 | Per-stage timeout in seconds |
healthcheck_cron | No | 15,45 * * * * | Current-DNS-IP probe; launches a full reselection on failure |
log_file | No | /tmp/cloudflare-bestip.log | Plain runtime log path (/tmp does not survive container recreation) |
-n: Latency test threads; more threads = faster testing, but don't set too high on weak devices (routers); default 200, max 1000-dn: Download test count; number of IPs to test from lowest latency; default 10. When -sl parameter is configured, download testing stops when the number of IPs meeting the download speed threshold reaches the -dn value. Recommended: 2-sl: Minimum speed threshold in MB/s (uses the slowest business-valid current record, or 1 MB/s without a baseline)-tl: Maximum latency threshold in ms (uses the slowest business-valid current record, with a 20 ms floor and 100 ms fallback)Note: -sl and -tl are replaced by the dynamic baseline values described above. Other speedtest_para options are preserved.
ws_probe_host and ws_probe_path.-dd) and take the first ws_probe_candidate_limit results.101 enters the usable pool.TCP×0.3 + TLS×0.2 + WS×0.5; latency and download speed provide later tie-breaking.101.
host_nameis the DDNS record being written.ws_probe_hostis the actual Cloudflare application Host/SNI. They may differ, so the program does not guess this mapping.
Note: CloudflareSpeedTest tool workflow:
# Offline regression tests; no Cloudflare credentials required
bash tests/business_probe_test.sh
bash tests/main_flow_test.sh
bash tests/healthcheck_test.sh
# Local test script
./test.sh
# Build and push to Docker Hub (interactive)
./build-and-push.sh
[2026-08-10 04:38:00] [INFO] Business probe 162.159.44.212: TLS SNI OK (www.example.com)
[2026-08-10 04:38:00] [WARN] Business probe 162.159.44.212: WS FAIL (HTTP 403: error code: 1034)
[2026-08-10 04:38:01] [SUCCESS] Business probe 172.64.229.53: WS 101 OK (TCP 20.00ms, TLS 55.00ms, WS 105.00ms, score 69.50)
[2026-08-10 04:38:01] [SUCCESS] Selected 1 business-valid IP(s) for DNS:
2026-02-06 14:00:00 Updating Cloudflare IP list...
2026-02-06 14:00:01 ✓ Successfully updated IP list from Cloudflare (14 ranges)
Or on network error:
2026-02-06 14:00:00 Updating Cloudflare IP list...
2026-02-06 14:00:10 ⚠ Failed to download IP list from Cloudflare
2026-02-06 14:00:10 → Using backup IP list
Attempting to download CloudflareSpeedTest v2.3.0...
Download successful, extracting...
✓ CloudflareSpeedTest v2.3.0 installed successfully
CloudflareSpeedTest is ready
Attempting to download CloudflareSpeedTest v9.9.9...
⚠ Failed to download CloudflareSpeedTest v9.9.9
⚠ Reason: Network error or version not found
→ Using fallback version v2.3.4 (pre-installed)
✓ Fallback to CloudflareSpeedTest v2.3.4 successfully
CloudflareSpeedTest is ready
2026-02-06 12:00:00 Getting current DNS A record...
2026-02-06 12:00:01 Found current DNS IP: 1.1.1.1
2026-02-06 12:00:01 Performing baseline test on current IP: 1.1.1.1
2026-02-06 12:00:15 Baseline test results - Latency: 45.23ms, Speed: 35.67MB/s
2026-02-06 12:00:15 Using dynamic test parameters - Latency threshold: 45ms, Speed threshold: 35MB/s
2026-02-06 12:00:15 Start execute the speedtest with parameters: -n 800 -dn 1 -sl 35 -tl 45
2026-02-06 12:05:30 Best test result - IP: 1.0.0.1, Latency: 42.10ms, Speed: 38.21MB/s
2026-02-06 12:05:30 New IP is better than current baseline, will update DNS
2026-02-06 12:05:31 Successfully added dns: your.domain.com with ip address: 1.0.0.1
Issues and Pull Requests are welcome!
MIT License
Content type
Image
Digest
sha256:4ecf8b542…
Size
15.8 MB
Last updated
about 1 month ago
docker pull deepbluethought/cloudflarespeedtestddns