Suppose we now have the domain name <your-domain>. We can use the API provided by a DNS provider, using Cloudflare as an example, to periodically update the IPv6 address.
Log in to https://dash.cloudflare.com, add your domain to Cloudflare, and replace the DNS nameservers at your domain registrar with the nameservers provided by Cloudflare.
Then add the following record in the DNS management panel for your domain:
textType Name IPv6 Address Proxy Status TTL AAAA www <any value> DNS only 2 minutes
On the Cloudflare dashboard, open the overview page for the domain and obtain your API token. Also note the Zone ID of your domain.
Install the required software on Debian-based systems:
bashsudo apt install curl jq
Create the following script at /usr/local/bin/ddns-ipv6.sh. Be sure to modify the configuration section:
bash#!/bin/bash
LOGFILE="/var/log/ddns-ipv6.log"
LAST_IP_FILE="/var/tmp/last_ipv6.txt"
touch $LOGFILE
chmod 644 $LOGFILE
echo "=== $(date) Starting DDNS update ===" >> $LOGFILE
# Configuration
CF_API_TOKEN="<your Cloudflare API Token>"
CF_ZONE_ID="<your Zone ID>"
RECORD_NAME="<your domain>"
NET_INTERFACE="enp0s31f6" # Replace with your actual network interface
TTL=120
# Logging function
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOGFILE
}
# Try multiple methods to obtain a reliable public IPv6 address
get_ipv6() {
local ip_system=$(ip -6 addr show dev $NET_INTERFACE scope global \
| grep -E 'global (dynamic|mngtmpaddr|temporary|permanent)' \
| awk '/inet6/{print $2}' | cut -d'/' -f1 | head -1)
local ip_api=$(curl -6 --connect-timeout 5 -s https://api64.ipify.org || \
curl -6 --connect-timeout 5 -s https://ipv6.icanhazip.com)
[ -n "$ip_system" ] && echo $ip_system || echo $ip_api
}
# Get the current IPv6 address
IP=$(get_ipv6)
if [ -z "$IP" ]; then
log "Error: Unable to obtain a valid IPv6 address"
exit 1
fi
log "Detected IPv6 address: $IP"
# Read the previous IP address
if [ -f "$LAST_IP_FILE" ]; then
LAST_IP=$(cat "$LAST_IP_FILE")
if [ "$IP" = "$LAST_IP" ]; then
log "IPv6 address has not changed; no update required ($IP)"
echo "=== $(date) Finished DDNS update ===" >> $LOGFILE
exit 0
else
log "IPv6 address change detected: $LAST_IP → $IP"
fi
else
log "First run: no previous IP address found"
fi
# Get the DNS record ID
RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?type=AAAA&name=$RECORD_NAME" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json")
SUCCESS=$(echo "$RESPONSE" | jq -r '.success')
if [ "$SUCCESS" != "true" ]; then
ERRORS=$(echo "$RESPONSE" | jq -r '.errors[]?.message')
log "Error: Failed to retrieve record ID - $ERRORS"
exit 1
fi
RECORD_ID=$(echo "$RESPONSE" | jq -r '.result[0].id')
if [ -z "$RECORD_ID" ] || [ "$RECORD_ID" = "null" ]; then
log "Error: Record $RECORD_NAME was not found. Please create this AAAA record in Cloudflare first."
exit 1
fi
log "Record ID found: $RECORD_ID"
# Update the DNS record
UPDATE_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data "{\"type\":\"AAAA\",\"name\":\"$RECORD_NAME\",\"content\":\"$IP\",\"ttl\":$TTL,\"proxied\":false}")
UPDATE_SUCCESS=$(echo "$UPDATE_RESPONSE" | jq -r '.success')
if [ "$UPDATE_SUCCESS" = "true" ]; then
NEW_CONTENT=$(echo "$UPDATE_RESPONSE" | jq -r '.result.content')
log "Record updated successfully! $RECORD_NAME → $NEW_CONTENT"
echo "$IP" > "$LAST_IP_FILE" # Save the new IP address
else
ERRORS=$(echo "$UPDATE_RESPONSE" | jq -r '.errors[]?.message')
log "Error: Failed to update the DNS record - $ERRORS"
exit 1
fi
echo "=== $(date) Finished DDNS update ===" >> $LOGFILE
Then make the script executable:
bashchmod +x /usr/local/bin/ddns-ipv6.sh
Open your crontab:
bashcrontab -e
Add the following line:
bash*/5 * * * * /usr/local/bin/ddns-ipv6.sh >/var/log/ddns-ipv6.log 2>&1
This checks for IPv6 address changes every five minutes and automatically updates the DNS record when necessary.