A lightweight Cloudflare Dynamic DNS shell script.
- Dual Stack Support: Support for both IPv4 and IPv6;
- Multi-Record Support: Support for updating multiple records simultaneously;
- Smart Monitoring: Only updates DNS records when IP address changes;
- Auto Caching: Automatically caches DNS records and zone information for improved performance;
- Multiple Authentication Support: Support for both Cloudflare API Token and Legacy API Key authentication;
- Proxy Protocol Support: Support for configuring Socks proxy for API requests;
- Systemd Support: Provides service/timer examples and dynamic user support;
- Telegram Push: Highly readable Telegram notification push;
- CSV Logging: Automatic logging of DNS updates to CSV file for history tracking and analysis;
- Hook Commands: Execute custom commands when IPv4 or IPv6 addresses change;
- Nix Package: Easy installation through Nix package manager with all dependencies included;
- Flexible Configuration: Support for command line parameter passing and environment variable configuration;
Install directly from this repository using Nix flakes:
nix profile install github:fernvenue/cloudflare-ddns
After installation, you can run the script using:
cloudflare-ddns --help
The Nix package automatically includes all required dependencies (curl, jq, bash, etc.) and ensures they are available at runtime.
Get the script:
curl -o /usr/local/bin/cloudflare-ddns.sh https://raw.githubusercontent.com/fernvenue/cloudflare-ddns/refs/heads/master/cloudflare-ddns.sh
chmod +x /usr/local/bin/cloudflare-ddns.sh
Make sure the corresponding domain already has records before running, otherwise the script cannot find the records to update.
For detailed help information, you can use:
cloudflare-ddns --help
# or if using manual installation:
./cloudflare-ddns.sh --help
CLOUDFLARE_API_TOKEN
: API Token of your Cloudflare account (recommended);CLOUDFLARE_API_KEY
: Global API Key of your Cloudflare account;CLOUDFLARE_RECORD_NAMES
: Target record names, such asddns.example.com
orddns01.example.com,ddns02.example.com
;CLOUDFLARE_RECORD_TYPES
: Record types, can be4
(A) or6
(AAAA), corresponds one-to-one with record names, such as4,6,4
;CLOUDFLARE_USER_MAIL
: The email address of your Cloudflare account;CLOUDFLARE_ZONE_NAME
: Zone name, such asexample.com
;OUTBOUND_INTERFACE
: Optional, used to specify the network interface;SOCKS_ADDR
: Optional, used to configure Socks proxy for API requests (Cloudflare and Telegram), IP detection does not go through this proxy;SOCKS_PORT
: Optional, corresponding Socks proxy port;TELEGRAM_BOT_ID
: Optional, Telegram bot ID;TELEGRAM_CHAT_ID
: Optional, Telegram target chat for push notifications;CUSTOM_TELEGRAM_ENDPOINT
: Optional, used to customize the API domain used for Telegram push;ENABLE_CSV_LOG
: Optional, enable CSV logging (default:true
), set tofalse
to disable;IPV4_HOOK_COMMAND
: Optional, command to execute when IPv4 address changes;IPV6_HOOK_COMMAND
: Optional, command to execute when IPv6 address changes;FORCE_UPDATE
: Force update, update DNS records even if IP hasn't changed;
--cloudflare-api-token TOKEN
=$CLOUDFLARE_API_TOKEN
--cloudflare-api-key KEY
=$CLOUDFLARE_API_KEY
--cloudflare-record-names NAMES
=$CLOUDFLARE_RECORD_NAMES
--cloudflare-record-types TYPES
=$CLOUDFLARE_RECORD_TYPES
--cloudflare-user-mail EMAIL
=$CLOUDFLARE_USER_MAIL
--cloudflare-zone-name NAME
=$CLOUDFLARE_ZONE_NAME
--outbound-interface IFACE
=$OUTBOUND_INTERFACE
--socks-addr ADDR
=$SOCKS_ADDR
--socks-port PORT
=$SOCKS_PORT
--telegram-bot-id ID
=$TELEGRAM_BOT_ID
--telegram-chat-id ID
=$TELEGRAM_CHAT_ID
--custom-telegram-endpoint DOMAIN
=$CUSTOM_TELEGRAM_ENDPOINT
--enable-csv-log BOOL
=$ENABLE_CSV_LOG
--ipv4-hook-command COMMAND
=$IPV4_HOOK_COMMAND
--ipv6-hook-command COMMAND
=$IPV6_HOOK_COMMAND
--force-update
=$FORCE_UPDATE
Refer to cloudflare-ddns.service
and cloudflare-ddns.timer
for standard systemd service and systemd timer examples.
The script automatically logs all DNS record updates to a CSV file for history tracking and analysis. This feature is enabled by default.
CSV File Location: The CSV file history.csv
is created in the same directory as the configuration data:
- If
STATE_DIRECTORY
is set:$STATE_DIRECTORY/history.csv
- If
/var/lib
is writable:/var/lib/cloudflare-ddns/history.csv
- If
$HOME
is available:$HOME/.cache/cloudflare-ddns/history.csv
- Fallback:
/tmp/cloudflare-ddns/history.csv
CSV Format: The CSV file contains the following columns:
Timestamp
: RFC3339 formatted timestamp of the updateZone Name
: Cloudflare zone name (e.g.,example.com
)Record Name
: DNS record name (e.g.,ddns.example.com
)Record Type
: DNS record type (A
orAAAA
)Old IP
: Previous IP address (empty for new records)New IP
: New IP address after updateBackup API Used
: Whether backup IP detection service was used (true
/false
)
Example CSV content:
Timestamp,Zone Name,Record Name,Record Type,Old IP,New IP,Backup API Used
2025-07-09T10:30:45+08:00,example.com,ddns.example.com,A,192.168.1.100,203.0.113.42,false
2025-07-09T10:30:45+08:00,example.com,ddns.example.com,AAAA,,2001:db8::1,false
Disable CSV Logging: To disable CSV logging, set the environment variable ENABLE_CSV_LOG=false
or use the command line option --enable-csv-log false
.
The script supports executing custom commands (hooks) when IP addresses change. This allows you to trigger additional actions when your IPv4 or IPv6 address updates.
Hook Types:
- IPv4 Hook: Executed when any IPv4 (A record) address changes
- IPv6 Hook: Executed when any IPv6 (AAAA record) address changes
Configuration:
- Set via environment variables:
IPV4_HOOK_COMMAND
andIPV6_HOOK_COMMAND
- Set via command line:
--ipv4-hook-command
and--ipv6-hook-command
Hook Environment Variables: When a hook is executed, the script provides the following environment variables:
CLOUDFLARE_DDNS_IP_VERSION
: IP version ("4" for IPv4, "6" for IPv6)CLOUDFLARE_DDNS_OLD_IP
: Previous IP addressCLOUDFLARE_DDNS_NEW_IP
: New IP address after updateCLOUDFLARE_DDNS_ZONE_NAME
: Cloudflare zone nameCLOUDFLARE_DDNS_RECORD_NAMES
: Comma-separated list of updated record namesCLOUDFLARE_DDNS_TIMESTAMP
: RFC3339 formatted timestamp of the update
Hook Execution Behavior:
- Hooks run after DNS records are successfully updated
- Hook output is suppressed (redirected to
/dev/null
) - Hook failures do not affect the main script execution
- Only success/failure status is logged
- Hooks execute in parallel for different IP versions
Example Hook Commands:
Update Hurricane Electric DNS:
export IPV4_HOOK_COMMAND="curl -s -4 'https://dyn.dns.he.net/nic/update?hostname=example.com&password=your-password&myip=\$CLOUDFLARE_DDNS_NEW_IP'"
Send webhook notification:
export IPV4_HOOK_COMMAND="curl -X POST https://webhook.example.com/ip-changed -H 'Content-Type: application/json' -d '{\"ip\":\"\$CLOUDFLARE_DDNS_NEW_IP\",\"type\":\"ipv4\"}'"
Execute custom script:
export IPV4_HOOK_COMMAND="/path/to/your/script.sh"
export IPV6_HOOK_COMMAND="/path/to/your/ipv6-script.sh"
Update multiple services:
export IPV4_HOOK_COMMAND="curl -s 'https://service1.com/update?ip=\$CLOUDFLARE_DDNS_NEW_IP' && curl -s 'https://service2.com/api/ip' -d 'ip=\$CLOUDFLARE_DDNS_NEW_IP'"
The script requires the following tools to be installed on your system:
- curl - For sending HTTP requests to Cloudflare API and IP detection services;
- jq - For JSON parsing and configuration file operations;
- awk - For text processing (usually pre-installed);
- grep - For pattern matching (usually pre-installed);
- date - For timestamp generation (usually pre-installed);
The following examples are some common use cases, for reference only. For production environment deployment, it is recommended to use with systemd service and systemd timer.
Update a single A record with API token:
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com" \
--cloudflare-record-types "4"
Update a single AAAA record with API token:
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ipv6.example.com" \
--cloudflare-record-types "6"
Update both A and AAAA records for the same domain (note the repeated domain name):
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com,ddns.example.com" \
--cloudflare-record-types "4,6"
In this example:
- First
ddns.example.com
gets A record (IPv4) - Second
ddns.example.com
gets AAAA record (IPv6)
Update multiple records with the same type (IPv4 only):
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com,api.example.com,home.example.com" \
--cloudflare-record-types "4,4,4"
Update different records with different types (each record name corresponds to each record type):
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "api.example.com,ipv6.example.com,home.example.com" \
--cloudflare-record-types "4,6,4"
In this example:
api.example.com
gets A record (IPv4)ipv6.example.com
gets AAAA record (IPv6)home.example.com
gets A record (IPv4)
export CLOUDFLARE_API_TOKEN="your-cloudflare-api-token"
export CLOUDFLARE_USER_MAIL="[email protected]"
export CLOUDFLARE_ZONE_NAME="example.com"
export CLOUDFLARE_RECORD_NAMES="api.example.com,ipv6.example.com,home.example.com"
export CLOUDFLARE_RECORD_TYPES="4,6,4"
./cloudflare-ddns.sh
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com" \
--cloudflare-record-types "4" \
--telegram-bot-id "123456789:ABCdefGHIjklMNOpqrsTUVwxyz" \
--telegram-chat-id "-1001234567890"
Use custom Telegram API domain:
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com" \
--cloudflare-record-types "4" \
--telegram-bot-id "123456789:ABCdefGHIjklMNOpqrsTUVwxyz" \
--telegram-chat-id "-1001234567890" \
--custom-telegram-endpoint "my-telegram-api.example.com"
Use SOCKS proxy for API requests:
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com" \
--cloudflare-record-types "4" \
--socks-addr "[::1]" \
--socks-port "1080"
Force update even if IP address hasn't changed:
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com" \
--cloudflare-record-types "4" \
--force-update
Disable CSV logging for DNS updates:
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com" \
--cloudflare-record-types "4" \
--enable-csv-log false
Execute custom commands when IP addresses change:
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com,ddns.example.com" \
--cloudflare-record-types "4,6" \
--ipv4-hook-command "curl -s -4 'https://dyn.dns.he.net/nic/update?hostname=ddns.example.com&password=your-he-password&myip=\$CLOUDFLARE_DDNS_NEW_IP'" \
--ipv6-hook-command "curl -s -6 'https://dyn.dns.he.net/nic/update?hostname=ddns.example.com&password=your-he-password&myip=\$CLOUDFLARE_DDNS_NEW_IP'"
export IPV4_HOOK_COMMAND="curl -s 'https://webhook.example.com/ipv4-changed' -d 'ip=\$CLOUDFLARE_DDNS_NEW_IP'"
export IPV6_HOOK_COMMAND="/path/to/custom/ipv6-script.sh"
./cloudflare-ddns.sh \
--cloudflare-api-token "your-cloudflare-api-token" \
--cloudflare-user-mail "[email protected]" \
--cloudflare-zone-name "example.com" \
--cloudflare-record-names "ddns.example.com,ddns.example.com" \
--cloudflare-record-types "4,6"