🚀 Executive Summary

TL;DR: Manually verifying global DNS changes is a tedious, error-prone, and time-consuming process for engineers. This article presents a Python script utilizing the `dnspython` library to automate querying geographically diverse public DNS servers, providing a global snapshot of DNS propagation status in seconds.

🎯 Key Takeaways

  • The `dnspython` library provides a clean, high-level interface for making programmatic DNS queries in Python.
  • Querying a geographically diverse list of public DNS servers (e.g., Google, Cloudflare, OpenDNS) is crucial for obtaining a comprehensive global view of DNS propagation.
  • Using a Python `set` to analyze query results efficiently identifies inconsistencies, indicating whether DNS propagation is still in progress or complete across different resolvers.
  • Setting a low TTL (Time to Live) on DNS records well in advance of a migration is critical for faster propagation and more timely consistency checks.
  • Automating the script with tools like cron and integrating notification systems can transform it into a ‘set and forget’ monitoring solution for DNS migrations.

Monitor DNS Propagation Changes globally using Python

Monitor DNS Propagation Changes globally using Python

Alright, let’s talk about something that used to be a real time-sink for me: verifying DNS changes. I remember one specific migration where we were switching a critical API endpoint to a new load balancer. The CNAME change was simple, but I spent the next two hours SSH’ing into various jump boxes around the world, running `dig` and `nslookup` commands over and over. I was paranoid about flipping the switch before our users in Asia saw the updated record. It was tedious, error-prone, and frankly, a waste of engineering time.

That’s when I decided to automate it. A simple Python script now gives me a global snapshot in seconds, and I get that time back for more important work. Today, I’ll walk you through how to build that exact tool. It’s a straightforward script that delivers a ton of value and peace of mind.

Prerequisites

Before we dive in, make sure you have a few things ready. This won’t take long.

  • Python 3 installed on your machine.
  • Basic familiarity with DNS record types like A, AAAA, CNAME, and MX.
  • Access to a command-line terminal.
  • We’ll be using a third-party library, so you’ll need the ability to install Python packages.

The Step-by-Step Guide

Step 1: Setting Up Your Project

First things first, let’s get our environment in order. I’ll skip the standard virtual environment setup commands (`mkdir`, `python3 -m venv`, etc.) since you likely have your own workflow for that. The key part is to create an isolated environment for this project.

Once your environment is active, you’ll need to install the library that does all the heavy lifting for us: `dnspython`. You can install it using pip. Just run `pip install dnspython` in your terminal. This library is fantastic—it gives us a clean, high-level interface for making DNS queries.

Step 2: The Core Logic – Querying Multiple Resolvers

Now, let’s get to the code. The main idea is to query a list of geographically diverse public DNS servers for the same record and see if their answers match. If they all return the same new IP address, we can be confident our change has propagated widely.

Create a file named `check_dns.py` and let’s start building.


import dns.resolver
import time

# A geographically diverse list of public DNS servers
# In my production setups, this list is often much longer.
GLOBAL_DNS_SERVERS = [
    '8.8.8.8',        # Google (USA)
    '1.1.1.1',        # Cloudflare (Global)
    '208.67.222.222', # OpenDNS (USA)
    '9.9.9.9',        # Quad9 (Switzerland)
    '8.26.56.26',     # Comodo Secure DNS (USA)
    '198.101.242.72', # Norton ConnectSafe (USA)
    '23.253.163.53',  # OpenNIC (Varies)
]

def check_global_propagation(domain, record_type='A'):
    """
    Queries a list of global DNS servers for a specific domain and record type.
    """
    print(f"--- Starting DNS propagation check for '{domain}' [{record_type}] ---\n")
    
    results = {}
    
    for server in GLOBAL_DNS_SERVERS:
        resolver = dns.resolver.Resolver()
        resolver.nameservers = [server]
        
        try:
            # We set a short timeout to avoid waiting forever on a slow server.
            answer = resolver.resolve(domain, record_type, lifetime=5)
            # Store the first result as a representative answer.
            # For records that can have multiple values (like A records for round-robin),
            # we'll just check the first one for consistency in this simple script.
            results[server] = str(answer[0])
            print(f"âś… {server.ljust(15)} -> {results[server]}")
        except dns.resolver.NoAnswer:
            results[server] = "No Answer"
            print(f"⚠️ {server.ljust(15)} -> No Answer")
        except dns.resolver.NXDOMAIN:
            results[server] = "NXDOMAIN"
            print(f"❌ {server.ljust(15)} -> Domain Not Found (NXDOMAIN)")
        except dns.exception.Timeout:
            results[server] = "Timeout"
            print(f"⏰ {server.ljust(15)} -> Query Timed Out")
        
        # A small delay to be polite to the DNS servers.
        time.sleep(0.5)
        
    return results

if __name__ == "__main__":
    # --- Configuration ---
    DOMAIN_TO_CHECK = "www.google.com"
    RECORD_TYPE_TO_CHECK = "A" # Can be 'A', 'CNAME', 'MX', etc.
    # -------------------

    propagation_results = check_global_propagation(DOMAIN_TO_CHECK, RECORD_TYPE_TO_CHECK)

Here’s what’s happening in the script:
1. We import the necessary `dns.resolver` module.
2. We define `GLOBAL_DNS_SERVERS`, our list of targets. These servers are operated by different organizations in different locations, giving us a good sample of what the world sees.
3. The `check_global_propagation` function iterates through each server.
4. For each server, we create a new `Resolver` instance and explicitly tell it to *only* use that server (`resolver.nameservers = [server]`). This is the key to isolating our queries.
5. We wrap the query in a `try…except` block to gracefully handle common DNS errors like `NoAnswer` (the record exists but not of the type we asked for) or `NXDOMAIN` (the domain doesn’t exist at all).
6. We store the results and print them out in a clean format.

Step 3: Analyzing the Results for Consistency

Getting the data is great, but the real value is in automatically analyzing it. Let’s add a function to tell us if propagation is complete.

Append this code to your `check_dns.py` script:


def analyze_results(results):
    """
    Analyzes the dictionary of results to check for inconsistencies.
    """
    # Using a set is a clever and efficient way to find unique values.
    unique_responses = set(results.values())
    
    print("\n--- Analysis ---")
    if len(unique_responses) == 1:
        response = unique_responses.pop()
        if response in ["Timeout", "NXDOMAIN"]:
             print(f"🚨 Consistent Error: All servers reported '{response}'. Check your domain.")
        else:
             print(f"âś… Consistent: All servers returned the same result: {response}")
             print("Propagation appears to be complete!")
    else:
        print("⚠️ Inconsistent: Different servers are returning different results.")
        print("Propagation is likely still in progress.")
        for server, ip in results.items():
            print(f"  - {server.ljust(15)} sees: {ip}")

# Modify the main execution block to use the new function
if __name__ == "__main__":
    # --- Configuration ---
    DOMAIN_TO_CHECK = "www.techresolve.com" # Change this to a domain you manage
    RECORD_TYPE_TO_CHECK = "A"
    # -------------------

    propagation_results = check_global_propagation(DOMAIN_TO_CHECK, RECORD_TYPE_TO_CHECK)
    analyze_results(propagation_results)

This new `analyze_results` function is simple but effective. It puts all the values from our results dictionary into a `set`, which automatically removes duplicates. If the length of the set is 1, it means every server gave the same answer. If it’s more than 1, we know there’s a mix of old and new records out there.

Pro Tip: For mission-critical deployments, I integrate this logic with a notification system. If `len(unique_responses)` is greater than 1, the script sends an alert to a Slack channel. It’s a simple way to create a “set and forget” monitoring system during a DNS migration. You can stop staring at the screen and let the bot tell you when the job is done.

Step 4: Automating the Check

You can run this script manually whenever you make a change, but you can also schedule it. On a Linux system, you can use cron to run this check periodically. For example, to run it every hour, you could add a line like this to your cron configuration:

`0 * * * * python3 /path/to/your/project/check_dns.py`

Remember to use the full path to your script and the `python3` interpreter inside its virtual environment for reliability.

Common Pitfalls (Where I’ve Messed Up Before)

  • Ignoring TTL (Time to Live): This script doesn’t speed up DNS propagation; it just reports on its status. The propagation speed is determined by the TTL value you set on your DNS records. If you set a TTL of 24 hours, you’re going to see inconsistent results for up to 24 hours, and my script will correctly tell you that. I always recommend setting a low TTL (like 60-300 seconds) well in advance of a planned migration.
  • Firewall and Network Restrictions: The first time I ran a similar script from a corporate network, it failed completely. Why? The network firewall blocked outbound DNS queries (port 53) to any server except the company’s internal resolvers. Make sure you’re running this from a machine that has open access to the internet.
  • Querying Too Aggressively: Hitting public DNS servers in a tight loop without a delay can sometimes get your IP temporarily rate-limited. The small `time.sleep(0.5)` in the loop is a good practice to be a considerate internet citizen and avoid issues.

Conclusion

And there you have it. With a handful of Python code, you’ve built a powerful tool that provides global visibility into your DNS changes. This little utility has saved me countless hours and, more importantly, has given me the confidence to execute infrastructure changes smoothly and without guesswork.

Automating these small, repetitive checks is a cornerstone of effective DevOps. It frees you up to solve bigger, more interesting problems.

Happy scripting,

Darian Vance

Senior DevOps Engineer, TechResolve

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ How can I programmatically check if my DNS changes have propagated globally?

You can programmatically check global DNS propagation using a Python script with the `dnspython` library. The script queries a list of geographically diverse public DNS servers for a specific domain and record type, then analyzes the consistency of the responses to determine propagation status.

âť“ How does this Python script compare to manual `dig`/`nslookup` or online DNS checkers?

This Python script automates and consolidates global DNS checks, eliminating the need for manual SSH into various jump boxes and repetitive `dig`/`nslookup` commands. Unlike one-off manual checks or limited online tools, it provides a customizable, schedulable, and programmatic solution for continuous monitoring and integration with notification systems.

âť“ What are common implementation pitfalls when monitoring DNS propagation with this script?

Common pitfalls include ignoring the TTL (Time to Live) value on DNS records, which dictates propagation speed; encountering firewall or network restrictions blocking outbound DNS queries (port 53) to public resolvers; and querying DNS servers too aggressively without delays, which can lead to temporary rate-limiting.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading