A Python tool that samples IPs from Cloudflare CIDR lists, tests latency via cdn-cgi/trace, enriches results with geo info, and writes the best IPs to a plain-text file — scheduled automatically via Docker + supercronic.
- Samples IPs from built-in CIDR sources
- Concurrent latency testing with
asyncio(concurrency controlled byscan.concurrency) - Geo lookup via ip-api.com batch API
- Output format:
ip:port#CountryCode-Region(e.g.1.2.3.4:443#CN-Guangdong) - Scheduled execution using supercronic inside Docker
- Configuration via environment variables
- Optional GitHub sync via the GitHub Contents API
- Optional Cloudflare DNS sync for A records
docker compose up -dTo run locally with uv:
uv run python main.pyDefaults are defined by class-based config objects in config/config.py. EnvConfig reads environment variables once at startup, and AppConfig resolves them into the runtime models.Config used by the scanner.
SCAN_SOURCE is read from the environment and maps to built-in source files in config/source/*.txt; if unset or empty, cloudflare is used.
By default, each sampled IP uses a random port from 443,2053,2083,2087,2096,8443. Set SCAN_PORT=443 to force a fixed port, or SCAN_PORT=443,8443 to use a smaller port pool. If SCAN_PORT is empty or contains no valid ports, the default random port pool is used.
| Section | Description |
|---|---|
scan |
Ports, concurrency, sample size |
schedule |
Cron expression and timezone for Docker scheduling |
output |
Output file path and max number of IPs to keep. Use output/... with Docker Compose so files land in the mounted ./output directory |
http |
Request timeout and retries |
geo |
ip-api.com batch query settings |
log |
Log level and optional log file path |
sync |
Optional GitHub and Cloudflare DNS sync settings |
Default schedule: 0 6 * * * in Asia/Shanghai timezone.
Environment overrides:
| Variable | Description |
|---|---|
SCAN_SOURCE |
Built-in source name from config/source/*.txt, e.g. cloudflare |
SCAN_PORT |
Fixed port; unset or invalid means random port |
SCAN_CONCURRENCY |
Number of concurrent latency test coroutines |
SCAN_TOTAL |
Number of sampled IPs |
SCAN_OUTPUT_PATH |
Output file path |
SCAN_OUTPUT_LIMIT |
Max number of IPs to keep |
SCHEDULE_CRON |
Cron expression |
SCHEDULE_TIMEZONE |
IANA timezone name |
LOG_LEVEL |
DEBUG, INFO, WARNING, ERROR |
SYNC_GITHUB_OWNER |
GitHub user or organization |
SYNC_GITHUB_REPO |
GitHub repository name |
SYNC_GITHUB_BRANCH |
Branch to update |
SYNC_GITHUB_REMOTE_PATH |
Target file path in repository |
SYNC_GITHUB_TOKEN |
GitHub token |
SYNC_CLOUDFLARE_SUB_DOMAIN |
DNS record name to update. Defaults to @ for the zone apex |
SYNC_CLOUDFLARE_LIMIT |
Max number of unique IPs to sync to Cloudflare DNS. Defaults to 10 |
SYNC_CLOUDFLARE_TOKEN |
Cloudflare API token with Zone/DNS edit permissions |
GitHub sync is disabled unless all required GitHub sync environment variables are set:
SYNC_GITHUB_OWNER=your-github-name
SYNC_GITHUB_REPO=your-repo
SYNC_GITHUB_BRANCH=main
SYNC_GITHUB_REMOTE_PATH=ips.txt
SYNC_GITHUB_TOKEN=github_pat_xxxWhen all five values are set, GitHub sync is enabled automatically.
Cloudflare DNS sync updates A records for the first zone returned by the Cloudflare API token. It deletes existing A records for the target record name, then creates one A record for each unique IP in the output file.
SYNC_CLOUDFLARE_TOKEN=your-cloudflare-api-tokenWhen SYNC_CLOUDFLARE_TOKEN is set, Cloudflare DNS sync is enabled automatically.
SYNC_CLOUDFLARE_SUB_DOMAIN is optional and defaults to @.
SYNC_CLOUDFLARE_LIMIT is optional and defaults to 10.
Pull the latest image:
docker pull ghcr.io/jwygithub/cloudflare-best-ip:latestRun with environment overrides:
docker run \
-e SCAN_SOURCE="cloudflare" \
-e SCAN_PORT="443" \
-e SCAN_CONCURRENCY="8" \
-e SCAN_TOTAL="30" \
-e SCAN_OUTPUT_PATH="output/result.txt" \
-e SCAN_OUTPUT_LIMIT="30" \
-e SCHEDULE_CRON="0 6 * * *" \
-e SCHEDULE_TIMEZONE="Asia/Shanghai" \
-e LOG_LEVEL="INFO" \
-e SYNC_GITHUB_OWNER="your-github-name" \
-e SYNC_GITHUB_REPO="your-repo" \
-e SYNC_GITHUB_BRANCH="main" \
-e SYNC_GITHUB_REMOTE_PATH="ips.txt" \
-e SYNC_GITHUB_TOKEN="github_pat_xxx" \
-e SYNC_CLOUDFLARE_LIMIT="10" \
-e SYNC_CLOUDFLARE_TOKEN="cloudflare_api_token_xxx" \
-v ./output:/app/output \
ghcr.io/jwygithub/cloudflare-best-ip:latestOmit SCAN_PORT to randomly select from the built-in port pool.
Container logs are plain text when captured by Docker or supercronic. ANSI color output is only enabled when Python logs directly to an interactive TTY, so scheduled runs do not emit escaped color sequences such as \x1b[32m.
Push a tag matching v* to trigger the build workflow:
git tag v1.0.0
git push origin v1.0.0The workflow builds multi-arch images (amd64 + arm64) and pushes them to GHCR.
ISC