#!/bin/bash # ============================================================================ # DYNAMIC DNS UPDATER FOR NAMECHEAP (MULTI-DOMAIN) # ============================================================================ # Configuration - Edit these values # ============================================================================ # Define your domains/hosts as an array of space-separated strings # Format: "HOSTNAME:DOMAIN:PASSWORD" # Use '@' for root domain, 'sub' for subdomain (e.g., 'home', 'video') DOMAINS=( "home:seanowiecki.com:d7b38fc4de474e81a79a309824aa8e2a" "video:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "frigate:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "survey:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "memos:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "openwebui:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "immich:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "matrix:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "minecraft:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "chat:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "bentopdf:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "home:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "nextcloud:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "obico:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "obsidian:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "qr:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "cachet:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "grafana:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "valheim:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "satisfactory:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "vpn:atomic6.net:2a687057401f4401b4248d1d31b2ba81" "salt:nase.dev:1fa7b1a67d404b339e38107db98c87f0" "gitea:nase.dev:1fa7b1a67d404b339e38107db98c87f0" ) # IP checking service (choose one that works reliably for you) IP_SERVICE="https://api.ipify.org" # Alternatives: "https://ifconfig.io/ip", "https://icanhazip.com" # ============================================================================ # Script Core - Do not edit below unless you know what you're doing # ============================================================================ # Get current public IP echo "[$(date)] Checking public IP..." CURRENT_IP=$(curl -s "$IP_SERVICE") if [ -z "$CURRENT_IP" ]; then echo "[$(date)] ERROR: Could not fetch public IP. Check network or IP service." exit 1 fi echo "[$(date)] Current public IP: $CURRENT_IP" # Process each domain entry for ENTRY in "${DOMAINS[@]}"; do # Split the entry into components IFS=":" read -r HOSTNAME DOMAIN PASSWORD <<< "$ENTRY" # Create a unique identifier for this host/domain combination RECORD_ID="${HOSTNAME}.${DOMAIN}" # File to store last IP for this specific record IP_FILE="/tmp/last_ip_${RECORD_ID//[^a-zA-Z0-9]/_}.txt" # Read last recorded IP if file exists if [ -f "$IP_FILE" ]; then LAST_IP=$(cat "$IP_FILE") else LAST_IP="" fi # Update only if IP has changed if [ "$CURRENT_IP" != "$LAST_IP" ]; then echo "[$(date)] IP changed for $RECORD_ID ($LAST_IP → $CURRENT_IP). Updating..." # Call Namecheap's API RESPONSE=$(curl -s "https://dynamicdns.park-your-domain.com/update?host=$HOSTNAME&domain=$DOMAIN&password=$PASSWORD&ip=$CURRENT_IP") # Check response for success if echo "$RESPONSE" | grep -q "0"; then echo "[$(date)] ✓ Successfully updated $RECORD_ID" echo "$CURRENT_IP" > "$IP_FILE" else # Extract error message from response ERROR=$(echo "$RESPONSE" | grep -o ".*" | sed 's/\(.*\)<\/Err1>/\1/') echo "[$(date)] ✗ Failed to update $RECORD_ID: $ERROR" fi # Optional: Log full response for debugging # echo "API Response: $RESPONSE" else echo "[$(date)] IP unchanged for $RECORD_ID ($CURRENT_IP). Skipping." fi done echo "[$(date)] DDNS update cycle completed."