You cut over your apex domain or primary API endpoint to a new server cluster. You update the DNS record in your cloud console, run dig api.example.com +short in your local shell, and see the new IP address. You announce that migration is complete.
Ten minutes later, your incident channel lights up.
A fifth of your global users are still hitting the old servers. Webhook callbacks from payment providers are failing, and mobile clients report intermittent ERR_NAME_NOT_RESOLVED errors.
To most engineers, "DNS propagation" sounds like a gradual wave washing over global networks. In reality, DNS is an asynchronous hierarchy of independent caches governed by differing RFC implementations and ISP behaviors.
Here are five real-world resolver traps that cause production traffic splits during migrations, and how to avoid them.
1. The Pre-Migration TTL Illusion
The most frequent migration mistake is changing the record and reducing the Time-to-Live (TTL) in the exact same step:
# Old record:
api.example.com. 86400 IN A 198.51.100.10
# Migration change:
api.example.com. 300 IN A 203.0.113.50
Reducing the TTL to 300 seconds during cutover does nothing for resolvers that already cached the previous record. Any resolver that queried api.example.com five minutes prior will continue routing traffic to the old IP (198.51.100.10) for the remaining 23 hours and 55 minutes of the original 86,400-second window.
Fix: Lower your TTL to 300 seconds at least 48 to 72 hours before your scheduled maintenance window. Only execute the cutover once existing caches have expired under the old TTL.
2. Negative Caching Traps (RFC 2308)
Suppose you are launching a new endpoint, such as v2-auth.example.com. While preparing the release an hour before launch, an engineer tests a deployment script or a CI job curls the URL to verify TLS certificates.
The authoritative nameserver correctly returns NXDOMAIN (Non-Existent Domain).
Under RFC 2308, recursive resolvers cache negative responses. The duration is dictated by the MINIMUM field in your zone's SOA record:
example.com. 3600 IN SOA ns1.example.com. hostmaster.example.com. (
2026092601 ; serial
7200 ; refresh
3600 ; retry
1209600 ; expire
3600 ; negative caching TTL (1 hour)
)
Even if you publish the new A record seconds later, resolvers that saw that early NXDOMAIN will reject all queries for the next hour without querying authoritative nameservers again.
3. Anycast PoP Divergence and Local Resolver Drift
Major public DNS providers (like Cloudflare 1.1.1.1 and Google 8.8.8.8) use BGP Anycast to announce the same IP from hundreds of Points of Presence (PoPs) worldwide.
When querying 1.1.1.1 from London, you hit a different node and cache than someone querying 1.1.1.1 in Tokyo or Frankfurt. Anycast providers do not synchronize cache entries globally in real time. One edge node may fetch the fresh record upon cache eviction, while another edge node in another region still has 15 minutes remaining on its TTL.
When verifying whether your DNS changes have reached global consensus, querying a multi-location DNS Propagation Checker allows you to inspect resolver status, response latencies, and regional IP discrepancies side-by-side rather than trusting your own shell's single hop.
4. EDNS0 Client Subnet (ECS) Routing Mismatches
If your infrastructure relies on GeoDNS or latency-based routing (Route 53, Cloudflare Load Balancing), your authoritative nameserver returns different IP addresses based on client geography.
Under RFC 7871 (EDNS Client Subnet), recursive resolvers send a truncated version of the client's IP (/24 for IPv4) to the authoritative nameserver to return the closest server.
However, privacy-conscious resolvers often strip ECS data entirely. In those cases, the authoritative server routes traffic based on the Anycast resolver's datacenter IP, not the end user's location. Testing DNS locally often hides routing mismatches that your production clients encounter abroad.
5. Application and OS-Level Stub Caching
Even when upstream resolvers update, internal application runtimes often hold onto stale IPs:
-
JVM: Historically cached successful DNS lookups forever (
networkaddress.cache.ttl = -1). In modern runtimes, it defaults to 30 seconds unless a security manager locks it indefinitely. -
Node.js: Standard
http.getcalls delegate togetaddrinfosynchronously in libuv, inheriting OS caching fromsystemd-resolvedornscd. - Docker / Kubernetes: Internal CoreDNS forwarders maintain their own upstream TTL rules.
Configure explicit DNS cache limits in long-running container runtimes before performing zero-downtime host migrations.
Safe Migration Protocol
To ensure clean zero-downtime cutovers:
- Lower TTLs in advance: Set TTLs to 300 seconds at least 48 hours prior to maintenance.
- Never pre-query unreleased subdomains: Prevent negative cache poisoning on recursive resolvers.
- Keep the old origin active: Maintain reverse proxy routing or a 301 redirect on the old cluster for at least 48 hours post-cutover.
- Monitor global propagation: Verify worldwide consistency with tools like Nutilz before shutting down legacy infrastructure.
Top comments (0)