Cloudflare DDNS


This guide provides a detailed explanation of how to set up Cloudflare Dynamic DNS (DDNS) for your self-hosted server. By following these steps, you’ll be able to maintain a consistent connection to your server, even if your IP address changes. 

Note: If you’re interested in creating a live website, please refer to my separate article on Nginx Manager for guidance.

1. Setting up Cloudflare:

  1. Sign up for a Cloudflare account if you don’t have one.
  2. Add your domain to Cloudflare and update your nameservers.

2. Getting Cloudflare Zone ID:

  1. Log in to your Cloudflare dashboard.
  2. Select your domain.
  3. On the overview page, scroll down to the “API” section.
  4. Your Zone ID will be displayed there.

3. Creating Cloudflare API Token:

  1. Go to your Cloudflare dashboard.
  2. Click on “My Profile” in the top right corner.
  3. Select “API Tokens” from the left sidebar.
  4. Click “Create Token”.
  5. Use the “Edit zone DNS” template or create a custom token with the following permissions:
    • Zone - DNS - Edit
    • Zone - Zone - Read
  6. Set the zone resources to include your specific domain.
  7. Create the token and copy it for later use.

4. Adding DNS Records:

  1. In your Cloudflare dashboard, go to the “DNS” tab.
  2. Click “Add record”.
  3. For the root domain:
    • Type: A
    • Name: @
    • IPv4 address: Your current IP (you can use a placeholder for now)
  4. For subdomains:
    • Type: A
    • Name: your subdomain (e.g., “blog” for blog.yourdomain.com)
    • IPv4 address: Your current IP (you can use a placeholder for now)
  5. Repeat for all desired subdomains.

5. Setting up the DDNS script:

This script offers the following benefits:

  1. All DDNS-related files are contained within the $HOME/.ddns/ directory.
  2. The script automatically creates the `.ddns
  3. Getting the current IP: The script uses curl -s http://ipv4.icanhazip.com to get the current IP. Alternatives include:
    • curl -s https://api.ipify.org
    • dig +short myip.opendns.com @resolver1.opendns.com
  4. Running the script:
    1. Save the script to a file, e.g., ddns.sh
#!/bin/bash

# Set the base directory
DDNS_DIR="$HOME/.ddns"

# Ensure the DDNS directory exists
mkdir -p "$DDNS_DIR"

# Load environment variables
source "$DDNS_DIR/.env"

# Cloudflare settings
ZONE_ID="${CF_ZONE_ID}"
API_TOKEN="${CF_API_TOKEN}"
DOMAIN="${CF_DOMAIN}"
SUBDOMAINS=(${CF_SUBDOMAINS})

# Log file location
LOG_FILE="$DDNS_DIR/ddns.log"

# Function to log messages
log_message() {
    echo "$(date): $1" >> "$LOG_FILE"
}

# Fetch the current public IP
CURRENT_IP=$(curl -s http://ipv4.icanhazip.com)
if [ -z "$CURRENT_IP" ]; then
    log_message "ERROR: Failed to obtain current public IP."
    exit 1
fi

# Function to update DNS record
update_dns_record() {
    local record_name=$1
    local record_id=$2
    local ip=$3

    UPDATE_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$record_id" \
         -H "Authorization: Bearer $API_TOKEN" \
         -H "Content-Type: application/json" \
         --data "{\"type\":\"A\",\"name\":\"$record_name\",\"content\":\"$ip\"}")

    if echo $UPDATE_RESPONSE | jq -e '.success'; then
        log_message "DNS record for $record_name updated successfully to $ip."
    else
        log_message "ERROR: Failed to update DNS record for $record_name."
        log_message "Response: $UPDATE_RESPONSE"
    fi
}

# Process each subdomain
for subdomain in "${SUBDOMAINS[@]}"; do
    FULL_DOMAIN="$subdomain.$DOMAIN"
    log_message "Processing $FULL_DOMAIN..."

    # Fetch the existing DNS record ID
    RECORD_RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=$FULL_DOMAIN" \
         -H "Authorization: Bearer $API_TOKEN" \
         -H "Content-Type: application/json")

    RECORD_ID=$(echo $RECORD_RESPONSE | jq -r '.result[0].id')
    EXISTING_IP=$(echo $RECORD_RESPONSE | jq -r '.result[0].content')

    if [ -z "$RECORD_ID" ] || [ -z "$EXISTING_IP" ]; then
        log_message "ERROR: Failed to fetch existing DNS record for $FULL_DOMAIN."
        continue
    fi

    # Compare and update if necessary
    if [ "$CURRENT_IP" != "$EXISTING_IP" ]; then
        update_dns_record $FULL_DOMAIN $RECORD_ID $CURRENT_IP
    else
        log_message "IP has not changed for $FULL_DOMAIN. No update required."
    fi
done

Now, you need to create a .env file in the $HOME/.ddns/ directory ($HOME/.ddns/.env) with the following content:

CF_ZONE_ID='your_zone_id_here'
CF_API_TOKEN='your_api_token_here'
CF_DOMAIN='yourdomain.com'
CF_SUBDOMAINS='subdomain1 subdomain2 subdomain3'
  1. Make it executable:

    chmod +x ddns.sh
    
  2. Run the script:

    ./ddns.sh
    
  3. Automating the script: To run the script automatically, you can add it to your crontab:

    1. Open the crontab editor:
      crontab -e
      
    2. Add a line to run the script every 5 minutes (adjust as needed):
      */5 * * * * /path/to/your/ddns.sh
      
  4. Checking the logs: You can view the log file to see the script’s activity:

    cat $HOME/.ddns/ddns.log
    

This setup allows you to easily update multiple subdomains with your current IP address, keeping your dynamic DNS records up to date with Cloudflare.