DEV Community

Cover image for Solved: Automating DNS Record Updates on Cloudflare using Python API
Darian Vance
Darian Vance

Posted on • Originally published at wp.me

Solved: Automating DNS Record Updates on Cloudflare using Python API

🚀 Executive Summary

TL;DR: Manual DNS record updates are a significant bottleneck in dynamic environments, leading to potential downtime and errors. This guide provides a robust, automated solution using a Python script and the Cloudflare API to detect and update ‘A’ records, ensuring domains always point to the correct public IP address.

🎯 Key Takeaways

  • Secure API interaction is crucial: Use Cloudflare’s scoped API Tokens with minimal permissions (Zone:Zone:Read, Zone:DNS:Edit) and store credentials securely via environment variables.
  • The Python script’s core logic involves fetching the current public IP (e.g., from api.ipify.org), retrieving the existing Cloudflare ‘A’ record, comparing the IPs, and updating the record via a Cloudflare API PUT request if a change is detected.
  • Automation is achieved by scheduling the Python script using a cron job, ensuring the necessary environment variables (CF_API_TOKEN, CF_ZONE_ID, CF_DNS_RECORD_NAME) are defined within the cron entry for reliable execution at regular intervals.

Automating DNS Record Updates on Cloudflare using Python API

Introduction

In modern infrastructure, managing DNS records can be a repetitive and error-prone task, especially in dynamic environments where IP addresses change frequently. Whether you’re running a home server with a dynamic public IP, managing ephemeral staging environments, or simply aiming for infrastructure-as-code principles, manual DNS updates are a bottleneck. This process is not only tedious but also introduces a risk of downtime if an IP changes and the corresponding DNS record isn’t updated promptly.

This tutorial solves that problem. We will walk you through building a robust, automated solution using Python and the powerful Cloudflare API. By the end, you’ll have a script that can automatically detect your server’s public IP address and update a specified Cloudflare DNS ‘A’ record, ensuring your domain always points to the correct location. This “set-it-and-forget-it” approach saves time, enhances reliability, and is a foundational skill for any DevOps professional.

Prerequisites

Before we begin, ensure you have the following ready:

  • A Cloudflare Account: You must have an active Cloudflare account with at least one domain name configured.
  • Python 3: Your system should have Python 3 installed. You can check this by running python3 --version.
  • Python requests Library: This library is essential for making HTTP requests to the Cloudflare API. We will install it in Step 2.
  • Administrative Access: You’ll need access to a terminal or command line on the machine whose public IP you want to track.

Step-by-Step Guide

Follow these steps to create and deploy your automated DNS updater.

Step 1: Obtain Your Cloudflare API Credentials

To interact with the Cloudflare API, you need three key pieces of information: your API Token, your Zone ID, and the DNS Record ID you wish to update.

1. Create an API Token:

For security, we’ll create a scoped API Token instead of using the global API Key. This limits the script’s permissions to only what is necessary.

  1. Log in to your Cloudflare dashboard.
  2. Navigate to “My Profile” > “API Tokens”.
  3. Click “Create Token” and use the “Create Custom Token” template.
  4. Give your token a descriptive name, like “Dynamic DNS Updater”.
  5. Under “Permissions”, grant the following two permissions:
    • Zone > Zone > Read
    • Zone > DNS > Edit
  6. Under “Zone Resources”, select the specific zone (your domain) that you want this token to manage.
  7. Click “Continue to summary” and then “Create Token”.

Important: Cloudflare will show you the token only once. Copy it immediately and store it securely. We will use an environment variable to manage it.

2. Find Your Zone ID:

The Zone ID uniquely identifies your domain within Cloudflare. You can find it on the “Overview” page of your selected domain in the Cloudflare dashboard, on the right-hand side under the “API” section.

3. Store Credentials Securely:

Never hardcode secrets in your script. We will use environment variables. Set them in your terminal session for now. We will make this permanent for automation later.

# Bash Script
export CF_API_TOKEN="your_copied_api_token_here"
export CF_ZONE_ID="your_zone_id_here"
export CF_DNS_RECORD_NAME="subdomain.yourdomain.com"
Enter fullscreen mode Exit fullscreen mode

Step 2: Setting Up Your Python Environment

First, create a dedicated directory for your project and navigate into it. It’s a best practice to use a Python virtual environment to manage dependencies and avoid conflicts.

# Bash Script
mkdir cloudflare_updater
cd cloudflare_updater
python3 -m venv venv
source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

With the virtual environment active, install the requests library:

# Bash Script
python3 -m pip install requests
Enter fullscreen mode Exit fullscreen mode

Step 3: Crafting the Python Update Script

Now, let’s write the core logic. Create a file named update_dns.py in your project directory and add the following code. The script is heavily commented to explain each part of the process.

# Python Script
import os
import requests
import json
import sys

# --- Configuration ---
# Load credentials and settings from environment variables for security.
API_TOKEN = os.getenv("CF_API_TOKEN")
ZONE_ID = os.getenv("CF_ZONE_ID")
RECORD_NAME = os.getenv("CF_DNS_RECORD_NAME")

# --- Validation ---
# Ensure all required configuration is present.
if not all([API_TOKEN, ZONE_ID, RECORD_NAME]):
    print("Error: Missing required environment variables (CF_API_TOKEN, CF_ZONE_ID, CF_DNS_RECORD_NAME)")
    sys.exit(1)

# --- API Setup ---
API_BASE_URL = f"https://api.cloudflare.com/client/v4/zones/{ZONE_ID}"
HEADERS = {
    "Authorization": f"Bearer {API_TOKEN}",
    "Content-Type": "application/json"
}

def get_public_ip():
    """Fetches the current public IP address from an external service."""
    try:
        response = requests.get("https://api.ipify.org?format=json", timeout=10)
        response.raise_for_status()
        return response.json()["ip"]
    except requests.RequestException as e:
        print(f"Error fetching public IP: {e}")
        return None

def get_dns_record(record_name):
    """Fetches a specific DNS record from Cloudflare."""
    try:
        url = f"{API_BASE_URL}/dns_records?type=A&name={record_name}"
        response = requests.get(url, headers=HEADERS, timeout=10)
        response.raise_for_status()
        records = response.json().get("result", [])
        return records[0] if records else None
    except requests.RequestException as e:
        print(f"Error fetching DNS record: {e}")
        return None

def update_dns_record(record_id, new_ip):
    """Updates the IP address for a given DNS record ID."""
    try:
        url = f"{API_BASE_URL}/dns_records/{record_id}"
        payload = {
            "type": "A",
            "name": RECORD_NAME,
            "content": new_ip,
            "ttl": 1 # 1 = Automatic TTL
        }
        response = requests.put(url, headers=HEADERS, data=json.dumps(payload), timeout=10)
        response.raise_for_status()
        print(f"Successfully updated DNS record for {RECORD_NAME} to {new_ip}")
        return True
    except requests.RequestException as e:
        print(f"Error updating DNS record: {e} - {e.response.text}")
        return False

def main():
    """Main logic to check and update the DNS record if necessary."""
    print("--- Starting DNS Update Check ---")

    public_ip = get_public_ip()
    if not public_ip:
        return # Exit if we can't get the public IP

    print(f"Current public IP: {public_ip}")

    dns_record = get_dns_record(RECORD_NAME)
    if not dns_record:
        print(f"Error: DNS record for '{RECORD_NAME}' not found.")
        return

    current_dns_ip = dns_record["content"]
    record_id = dns_record["id"]
    print(f"Current DNS IP for {RECORD_NAME}: {current_dns_ip}")

    if public_ip == current_dns_ip:
        print("IP addresses match. No update needed.")
    else:
        print("IP addresses do not match. Initiating update...")
        update_dns_record(record_id, public_ip)

    print("--- DNS Update Check Finished ---")

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Step 4: Automating the Script with Cron

To make this truly automated, we need to schedule the script to run at regular intervals. A cron job is perfect for this task. We’ll set it to run every 5 minutes.

First, find the full path to your python executable within the virtual environment and the script itself.

# Bash Script
# In your project directory
which python3
pwd
Enter fullscreen mode Exit fullscreen mode

This will give you paths like /home/user/cloudflare_updater/venv/bin/python3 and /home/user/cloudflare_updater.

Now, open your cron editor:

# Open your cron editor
crontab -e
Enter fullscreen mode Exit fullscreen mode

Add the following line to the file. This line defines the necessary environment variables directly, ensuring the script can access them, and then executes the script. Replace the paths with the ones you found.

# Cron Job Example
CF_API_TOKEN="your_copied_api_token_here"
CF_ZONE_ID="your_zone_id_here"
CF_DNS_RECORD_NAME="subdomain.yourdomain.com"

*/5 * * * * /home/user/cloudflare_updater/venv/bin/python3 /home/user/cloudflare_updater/update_dns.py
Enter fullscreen mode Exit fullscreen mode

Save and exit the editor. The cron daemon will now execute your script every five minutes, keeping your DNS record perfectly in sync with your public IP.

Common Pitfalls

If you run into issues, check these common problems first:

  1. Insufficient API Token Permissions: A 403 Forbidden error from the API almost always means your API Token lacks the required permissions. Double-check that it has both “Zone:Zone:Read” and “Zone:DNS:Edit” permissions for the correct zone. Regenerate the token if necessary.
  2. Incorrect Zone ID or Record Name: A 404 Not Found error or the script reporting that the record can’t be found usually points to a typo in your CF\_ZONE\_ID or CF\_DNS\_RECORD\_NAME environment variables. Ensure the record name is the fully qualified domain name (e.g., subdomain.yourdomain.com, not just subdomain).

Conclusion

Congratulations! You have successfully built a fully automated Dynamic DNS (DDNS) client using Python and the Cloudflare API. This script not only solves a practical problem but also serves as an excellent foundation for more advanced infrastructure automation. You’ve learned how to securely interact with a powerful API, manage credentials safely, and schedule tasks for reliable execution.

From here, you can expand the script’s capabilities. Consider adding more robust logging to a file, sending email or Slack notifications on failures, or adapting it to update other record types like AAAA for IPv6. This simple yet powerful automation is a key step toward managing your infrastructure as code.


Darian Vance

👉 Read the original article on TechResolve.blog


☕ Support my work

If this article helped you, you can buy me a coffee:

👉 https://buymeacoffee.com/darianvance

Top comments (0)