DNS Architecture: Anycast, GeoDNS and Global Server Load Balancing with Failover and TTL Strategy (2026)

DNS Architecture: Anycast, GeoDNS and Global Server Load Balancing with Failover and TTL Strategy (2026)

Anycast DNS, GeoDNS and GSLB: Failover and TTL Strategy (2026)

Most teams believe their DNS failover takes as long as the TTL they typed into the zone file. Set 60 seconds, expect traffic to move in about a minute. The arithmetic is comfortable and it is wrong in at least three places: the health check has to notice the failure first, resolvers are free to hold records longer than you asked, and the long tail of clients behind browsers, JVMs and mobile radios keeps connecting to a dead address well after the authoritative answer changed.

Anycast DNS is the layer that keeps the answering service alive, while GeoDNS and global server load balancing (GSLB) decide what the answers say. They solve different problems, fail in different ways, and are routinely confused in architecture reviews. The October 2025 AWS us-east-1 incident, which began as an empty DNS record, showed again how a single name can sit under an entire region’s availability.

This post separates the three mechanisms, shows how a query actually travels, and works the failover arithmetic with explicit assumptions. You will leave with a reference architecture, a TTL policy you can defend, dig commands for verifying behavior, and a list of failure modes with real incident evidence.

What this covers: the resolution path, anycast and BGP behavior, GeoDNS with EDNS Client Subnet, GSLB health checks, TTL and negative caching, failover time math, provider comparison, DNSSEC and DDoS posture, and failure modes.

Context and Background

The Domain Name System is a hierarchical, cached lookup service. A client application calls a stub resolver in the operating system, which forwards to a recursive resolver (your ISP, an enterprise server, or a public service). The recursive resolver walks the hierarchy: root servers, then the top-level domain servers, then the zone’s authoritative servers. Every answer carries a time to live (TTL), and every resolver along the way is allowed to cache it. That caching is what makes DNS cheap and fast, and also what makes it a poor real-time control plane.

Three distinct ideas get bundled under “DNS load balancing”. The first is availability of the DNS service itself, which is the job of anycast: the same IP address is announced from many locations through the Border Gateway Protocol (BGP), and the network delivers each query to a nearby instance. The second is answer steering: returning different records to different askers based on geography, latency or weights, which is GeoDNS and latency-based routing. The third is health-aware selection: withdrawing endpoints that are down, which is the core of GSLB. A single product may offer all three, but they are separate mechanisms with separate failure behavior.

The root zone shows how far anycast scales. The root-servers.org status page lists 13 root server letters, A through M, run by 12 independent operators, and reported 2,048 operational instances at the time of writing. Thirteen addresses (per address family) is a legacy of packet-size limits in the original protocol; the instance count is what anycast delivers on top of it. The operational guidance for running anycast services is in RFC 4786, and the same techniques are used by commercial authoritative providers.

Incumbents in this space fall into two groups. Hyperscaler DNS services (Amazon Route 53, Google Cloud DNS, Azure DNS) integrate with their own health checking and load balancers. Specialist providers (Cloudflare, NS1 which is now part of IBM, Akamai, and others) emphasize traffic-steering policy engines and large anycast footprints. Open-source authoritative servers such as PowerDNS, Knot DNS and CoreDNS can be paired with a GeoIP backend, though you then own the anycast and health-checking problem yourself.

If you already operate Kubernetes, remember that cluster DNS is a different animal with its own latency traps, covered in our Kubernetes DNS and CoreDNS ndots analysis. This post concerns the public, internet-facing layer. And since DNS steering only chooses a destination, the next layer matters too: how a load balancer picks a backend once traffic arrives, discussed in load balancing algorithms from power-of-two-choices to Maglev.

The thesis of this post is blunt. DNS is an excellent coarse, eventually consistent steering layer and a poor fast failover layer. Design it so that the failover you actually need does not depend on a TTL expiring, and use DNS to shape where the bulk of traffic lands under normal conditions.

Reference Architecture: Anycast Authority, Steering Policy and Health Signals

A resilient global DNS design has three layers. Anycast authoritative servers keep the zone reachable from everywhere. A steering policy (geography, latency, weights) chooses which record set each query receives. A health-check plane feeds endpoint status into that policy so dead targets stop being returned. Each layer fails independently, so each needs its own redundancy and its own test.

Anycast DNS reference architecture showing resolvers, BGP anycast authoritative sites, steering policy and health checks

Figure 1: Reference architecture. Recursive resolvers reach the nearest anycast authoritative site, which applies a steering policy fed by health checks before answering with regional endpoints.

In Figure 1, the recursive resolver sends its query to a single anycast address. BGP delivers it to whichever authoritative site is topologically closest. That site holds a full copy of the zone and the steering policy, consults the latest health status distributed from the health-check plane, and answers with the address of a regional load balancer. The client then connects to that regional endpoint, where an L4 or L7 balancer takes over. Notice that the DNS answer is only a hint about where to connect; the data path never touches the DNS servers.

How a query travels: stub, recursive, authoritative

Most operators underestimate how many independent caches sit between a user and the authoritative server. The browser keeps its own host cache. The operating system stub resolver may cache. A home router often runs a forwarder with a cache. The recursive resolver caches, frequently in a pool of many machines that do not share state, so two consecutive queries from one user can hit different cache nodes with different remaining TTLs. Large public resolvers are built this way, and your authoritative servers will see a refresh query from each cache node independently.

Each cache obeys the TTL on the record when it was fetched, counting down from the moment of that fetch. That means a record with a 300 second TTL can be served from a cache for the full 300 seconds even if the authoritative answer changed one second after the fetch. The effective staleness of the whole population is therefore uniformly spread between zero and one TTL after a change, assuming perfectly compliant resolvers. That “assuming” carries a lot of weight, and we return to it in the TTL section.

You can see the structure with dig. The +trace option performs iterative resolution from the root yourself, bypassing your recursive resolver’s cache, and shows the delegation chain:

# Walk the delegation chain from the root, no recursive cache involved
dig +trace www.example.com A

# Ask a specific recursive resolver and inspect remaining TTL (second column)
dig @1.1.1.1 www.example.com A +noall +answer

# Ask an authoritative server directly: the TTL shown is the configured one
dig @ns1.example.net www.example.com A +noall +answer +norecurse

Run the second command twice a few seconds apart. The TTL in the answer decrements, which tells you the answer came from cache. Run the third and the TTL stays at its configured value, which tells you the authoritative server is answering fresh. This simple comparison is the first diagnostic for any “why is my change not visible” ticket.

Anycast and BGP: what it gives you and what it does not

With anycast, several sites announce the same IP prefix. Routers choose the best BGP path for each source, so a given resolver usually reaches the same nearby site every time. For DNS this works unusually well, because a query and its response are a single UDP datagram pair. There is no long-lived connection that a mid-flow route change could break, which is the problem that makes anycast awkward for TCP-heavy services.

The failure behavior follows from how BGP works. If a site stops announcing the prefix, because an operator pulled it or an automated health daemon withdrew it, the routers converge on the next best path. Convergence is typically seconds to a few tens of seconds, not milliseconds, and during it some queries are lost and retried by resolvers. Because DNS clients retry and every authoritative zone lists multiple name servers, this is mostly invisible. The design goal is that the loss of any single site changes latency, not correctness.

Two caveats matter. First, anycast gives you proximity in network topology, which is not geographic distance. A resolver in one country can be routed to a site on another continent because of peering arrangements. Second, anycast is only as healthy as its withdrawal logic. A server that is running but returning wrong answers keeps its route and keeps receiving traffic unless something detects and withdraws it. The mirror image bit Meta in October 2021: the company’s own engineering write-up describes DNS servers that withdrew their BGP advertisements when they could not reach the data centers, so when the backbone went down the name servers vanished from the internet even though they were healthy. A safety mechanism designed to remove bad anycast nodes removed all of them at once.

Best practice for authoritative anycast is to publish name servers on at least two independent anycast networks, ideally operated by different providers, so that a routing or software fault in one does not take the zone offline. Multiple providers do add a synchronization problem, which we address in the failure-modes section.

GeoDNS and EDNS Client Subnet

GeoDNS answers the question “who is asking and where are they?” The difficulty is that the authoritative server never sees the end user. It sees the source IP of the recursive resolver. If the resolver is in the user’s ISP, the resolver location is a decent proxy. If the user uses a public resolver or a corporate resolver located elsewhere, the proxy can be wrong by thousands of kilometers.

EDNS Client Subnet (ECS), specified in RFC 7871, addresses this. A participating recursive resolver adds an EDNS0 option (option code 8) to its upstream query carrying a truncated prefix of the client’s address. The RFC recommends truncating IPv4 to 24 bits and IPv6 to 56 bits to limit privacy exposure. The authoritative server uses that prefix for geolocation and replies with a scope prefix length that tells the resolver how broadly the answer may be reused. A scope of /24 means the answer applies only to that /24; a scope of /0 means it applies to everyone.

ECS has real costs. The RFC itself says the feature should be off by default and enabled only where it clearly benefits clients, because it exposes part of the client address to every server in the resolution path, and because it substantially increases cache size and load: the resolver must keep a separate cached answer per scope prefix. Some large public resolvers support it, and others deliberately do not for privacy reasons, which means your GeoDNS accuracy varies by which resolver your users happen to have.

A practical consequence: GeoDNS is a statistical tool, not a guarantee. Plan for a fraction of users being steered to a non-optimal region and make sure the non-optimal region still works. Never use a geographic DNS answer for compliance enforcement such as data residency; use authenticated application-layer controls for that, and treat the DNS answer as a performance hint.

Latency-based routing takes a different approach. Instead of mapping a client location to a region, the provider measures network latency between its own measurement points and each of your regions, then returns the region with the lowest measured latency for the querying resolver’s network. It tracks reality better than a static geography table, but it is still based on the resolver, not the user, and it reacts on the provider’s measurement schedule, not instantly.

Deeper Analysis: GSLB, Health Checks and the TTL Reality

Global server load balancing is DNS steering plus health awareness. The authoritative layer holds several candidate endpoints per name and returns only those currently believed healthy, choosing among them by weight, geography, latency or priority. Everything interesting about GSLB lives in the gap between “the endpoint failed” and “every client stopped using it”. That gap is the sum of four delays, and engineers usually only account for one.

Sequence diagram of DNS failover timeline from endpoint failure through health check detection, record change, resolver cache expiry and client reconnect

Figure 2: Failover timeline. The outage a user feels is detection time, plus propagation to the authoritative plane, plus cache expiry, plus client behavior.

Health checks: detection is not instant

A health check is a probe, run from one or more vantage points, that declares an endpoint failed after a number of consecutive misses. In Amazon Route 53 the probe interval is either 10 seconds or 30 seconds, and the checkers in different locations do not coordinate. Route 53 considers an endpoint healthy when more than 18 percent of its health checkers report healthy, and the documentation notes that this value might change in a future release. The low threshold is deliberate: it prevents a single checker’s bad network path from declaring a healthy endpoint dead. It also means partial regional failures, where a minority of the internet cannot reach you, may not trigger failover at all.

Each individual check is bounded. For HTTP and HTTPS checks, Route 53 expects a TCP connection within 4 seconds and a 2xx or 3xx status within 2 seconds of connecting; with string matching the search string must appear in the first 5,120 bytes of the response body. A slow-but-alive endpoint that answers in three seconds is declared failing. That is usually what you want, but it means a latency spike during a deploy can trip failover.

What the probe tests is a design decision with consequences. A probe hitting a static /healthz that returns 200 whenever the process is up will keep a region in rotation while its database is unreachable. A probe that exercises the database will mark the region down whenever the database blips, and if every region shares that dependency, all regions go unhealthy together. Most providers handle “all endpoints unhealthy” by returning all records anyway (fail open), which is the right behavior, but check that yours does. In Route 53, alias records can also evaluate the health of the target they point at, which lets a record inherit the health of a load balancer rather than needing a separate probe.

Design the health endpoint in tiers. A shallow check answers “is the process alive?” and feeds the load balancer. A regional deep check answers “can this region serve a real request with its local dependencies?” and feeds DNS. Never let DNS health depend on a dependency that is global, or one failure will remove every region simultaneously.

TTL: the number everyone sets and nobody can enforce

TTL is a request, not a command. RFC 8767 on serving stale data explicitly permits resolvers to return expired records when they cannot reach the authoritative servers, suggests a stale-data retention of one to three days, and recommends answering stale data with a TTL of 30 seconds and falling back to it after a client response timer of about 1.8 seconds. That behavior improves resilience when your authoritative servers are down, which is good, but it means a stale record can outlive its TTL by design.

Other deviations are older and less documented. Some resolvers impose a minimum TTL floor, raising a 5 second TTL to 30 or 60 seconds to reduce load. Some enforce a cap, as the RFC notes with its 7-day ceiling on TTLs from authoritative servers. Some runtimes cache independently of DNS: a long-running Java process may cache resolved addresses for its lifetime depending on configuration and security-manager settings, and connection pools keep using established connections to an old IP regardless of any record change. These client-side effects are why a 60 second TTL can still leave a measurable tail of traffic flowing to a dead address for many minutes. Measure this tail on your own traffic; no vendor can tell you your tail.

Negative answers have their own timers. Under RFC 2308, the TTL of a “no such name” (NXDOMAIN) or “no such record” (NODATA) response is the lesser of the SOA record’s own TTL and its MINIMUM field, and resolvers should cap negative caching, with one to three hours suggested as working well. The practical trap: if a client queries a name before you create it, that NXDOMAIN gets cached, and the new record will not be visible to that resolver population until the negative TTL expires. Create records before anything references them, and keep the SOA negative TTL short (a few minutes) on zones where you create names dynamically.

Resolution failures are cached separately. RFC 9520 requires resolvers to cache failures such as SERVFAIL, REFUSED and timeouts for at least 1 second and no more than 300 seconds, with backoff recommended. If your authoritative service flaps, resolvers may keep failing to retry for up to five minutes even after you recover.

Worked failover arithmetic

Take a concrete illustrative configuration (the numbers are assumptions chosen for the example, not measurements): a 10 second health check interval, a failure threshold of 3 consecutive misses, a DNS record TTL of 60 seconds, and a population of well-behaved resolvers.

Detection takes up to threshold times interval, which is 3 x 10 = 30 seconds from the first failed probe, plus up to one interval of phase offset before the first failed probe lands, so roughly 30 to 40 seconds. The control plane then needs time to evaluate checker results and update the authoritative answer. Providers do not publish a hard figure for this step in the pages we reviewed, so treat it as an unquantified delay of several seconds and measure it. Then resolver caches expire uniformly over the next 0 to 60 seconds, so the average user sees the change after about 30 more seconds and the slowest compliant resolver after 60.

The compliant-resolver worst case is therefore about 40 + 60 = 100 seconds plus control-plane delay, and the mean is roughly 40 + 30 = 70 seconds. Now add the tail: resolvers with a minimum TTL floor of, say, 300 seconds, runtimes that never re-resolve, and connection pools holding open sockets. The mean barely moves, but the 99th percentile moves from minutes to potentially tens of minutes. This is why serious GSLB designs promise “most traffic moves within two to three minutes” and not “failover in 60 seconds”.

If you need to calculate a number for a service-level objective, a small model is more honest than a guess:

import random

def failover_seconds(interval=10, threshold=3, ttl=60,
                     control_plane=5, stragglers=0.03, straggler_s=900):
    """Illustrative Monte Carlo of time until one client stops using a dead IP.
    All parameters are assumptions; replace with measured values."""
    detect = threshold * interval + random.uniform(0, interval)
    cache_wait = random.uniform(0, ttl)          # compliant resolver cache expiry
    t = detect + control_plane + cache_wait
    if random.random() < stragglers:             # TTL floor, pinned runtime, pooled socket
        t += random.uniform(0, straggler_s)
    return t

samples = sorted(failover_seconds() for _ in range(100_000))
for q in (0.5, 0.9, 0.99):
    print(f"p{int(q*100)}: {samples[int(q*len(samples))]:.0f}s")

With these assumed inputs the median lands around one to one and a half minutes and the 99th percentile lands in the tens of minutes, driven almost entirely by the straggler fraction, not by the TTL. Lowering the TTL from 60 to 30 seconds shaves about 15 seconds off the median and does nothing for the stragglers. That is the counter-intuitive result worth putting in front of anyone who proposes a 5 second TTL as the fix.

Decision flow choosing between anycast, DNS steering and L7 load balancing based on failover speed and granularity needs

Figure 3: Choosing a mechanism. Fast, fine-grained failover belongs in L4 or L7 load balancers and anycast IPs; DNS handles coarse regional steering.

Choosing the right mechanism

Figure 3 summarizes a decision. If you need sub-second reaction to a backend failure, use a load balancer in front of the backends; DNS cannot help you. If you need to survive the loss of a whole region in a couple of minutes, health-checked DNS steering is appropriate. If you need the same IP to survive a regional loss without waiting for any cache, you want anycast at the service layer (for example a global network edge announcing your service address), which moves the problem from DNS caches to BGP convergence. The three compose rather than compete: anycast for the front door, DNS for regional placement, load balancers for per-request decisions.

Mechanism Reacts in Granularity Main failure mode Best used for
Anycast IP for the service seconds (BGP convergence) per network path wrong-site routing, route leaks TCP/UDP front doors, DNS servers
Health-checked DNS (GSLB) tens of seconds to minutes per name and region resolver and client caching tail regional failover, geo placement
GeoDNS or latency routing next cache refresh per resolver network resolver not near user latency optimization
L7 load balancer milliseconds to seconds per request shared dependencies, config errors backend failure, canaries
Client-side retry and failover per request per connection thundering herds masking transient errors

Provider capabilities and cost

Features differ more than marketing suggests. Pricing below is from the Route 53 public pricing page at the time of writing and will change, so confirm before budgeting. Route 53 charges $0.50 per month for each of the first 25 hosted zones, $0.40 per million standard queries for the first billion per month, $0.60 per million for latency-based routing queries, and $0.70 per million for geolocation and geoproximity queries. Health checks on AWS endpoints cost $0.50 per month for a basic check, with $1.00 per month per optional feature such as HTTPS or string matching, and checks on non-AWS endpoints cost $0.75 and $2.00 respectively.

The cost shape matters architecturally. Geo and latency queries carry a price premium over standard queries, but at 100 million queries per month the difference between standard ($40) and geolocation ($70) is small next to the engineering cost of an outage. Query volume is dominated by TTL: halving the TTL roughly doubles the authoritative query load from well-behaved resolvers, so a shorter TTL both costs more and adds load to your DNS plane during exactly the incidents when you want it to be calm.

Other providers differ in philosophy. Cloudflare and NS1 emphasize programmable steering policies and tie health data to filter chains, so you can express rules such as “prefer region A unless its health is below a threshold, then split between B and C by weight”. Consult each vendor’s current documentation for exact limits and pricing; we have not verified their numbers for this article and avoid quoting them.

Implementation sketch: failover records with health checks

The following Terraform fragment shows the primary and secondary failover pair on Route 53. It is a minimal sketch, not a production module; the hostnames are placeholders and the evaluate_target_health setting is what lets the alias inherit the load balancer’s health.

resource "aws_route53_health_check" "primary" {
  fqdn              = "origin-eu.example.com"
  port              = 443
  type              = "HTTPS"
  resource_path     = "/healthz/deep"
  request_interval  = 10
  failure_threshold = 3
}

resource "aws_route53_record" "www_primary" {
  zone_id         = var.zone_id
  name            = "www.example.com"
  type            = "A"
  set_identifier  = "primary-eu"
  health_check_id = aws_route53_health_check.primary.id
  failover_routing_policy { type = "PRIMARY" }
  alias {
    name                   = aws_lb.eu.dns_name
    zone_id                = aws_lb.eu.zone_id
    evaluate_target_health = true
  }
}

resource "aws_route53_record" "www_secondary" {
  zone_id        = var.zone_id
  name           = "www.example.com"
  type           = "A"
  set_identifier = "secondary-us"
  failover_routing_policy { type = "SECONDARY" }
  alias {
    name                   = aws_lb.us.dns_name
    zone_id                = aws_lb.us.zone_id
    evaluate_target_health = true
  }
}

Alias records to AWS load balancers are answered by the provider with the provider’s own short TTL behavior, so you do not set a TTL on them. For non-alias records you must choose one, which brings us to a policy.

A TTL policy you can defend

Treat TTL as a function of how often the record changes and how bad stale data is, not as a single zone-wide default. A workable starting policy follows. Records that point at stable infrastructure and almost never change, such as the name server set, MX records and verification TXT records, get hours (3,600 to 86,400 seconds). Steering records that GSLB rewrites on health changes get 30 to 60 seconds, accepting the extra query load. Records you are about to change deliberately, such as a migration cutover, get lowered in advance: drop the TTL at least one old-TTL period before the cutover, make the change, verify with dig against both an authoritative server and several public resolvers, then raise the TTL again.

The advance-lowering rule is the single most violated practice in migrations. If the current TTL is 24 hours and you lower it to 60 seconds an hour before cutover, caches that fetched the record 23 hours ago will still hold the old value for another hour. Lowering must precede the change by the old TTL, not the new one.

Verification uses a few commands. The +short form is good for scripts, and querying several resolvers side by side shows cache divergence:

for r in 1.1.1.1 8.8.8.8 9.9.9.9; do
  printf "%s " "$r"; dig @$r www.example.com A +noall +answer | awk '{print $2, $5}' | tr '\n' ' '; echo
done

# See which authoritative site answered (many anycast servers respond to this)
dig @ns1.example.net hostname.bind CH TXT +short

# Check what ECS scope an authoritative server returns for a given client subnet
dig @ns1.example.net www.example.com A +subnet=203.0.113.0/24

The hostname.bind and +subnet queries only work where the server supports them and where ECS handling is enabled. Many authoritative providers ignore or refuse them; treat a failure as a capability fact, not an error.

DNSSEC and DDoS posture

DNSSEC signs records so a validating resolver can detect forgery; it does not encrypt anything and does not change availability, but it changes your operations. Signed responses are larger, which raises the importance of handling UDP fragmentation and TCP fallback correctly. Key rollovers add a failure mode: a botched DS record update at the parent can make the entire zone return SERVFAIL for validating resolvers, and thanks to failure caching, recovery lags your fix by up to five minutes plus any cached DS or DNSKEY state. If you use GSLB that rewrites records dynamically, make sure your provider signs on the fly or at publication time, because every record change must produce a valid signature.

On the DDoS side, anycast is the primary defense for the authoritative plane: a volumetric attack is spread across many sites, so no single site sees the full volume, and sites can be drained or absorbed. This is exactly why DNS providers are attacked as a way to hit their customers. The 2016 attack on Dyn, which affected many major sites that depended on a single DNS provider, is the standard case study for single-provider risk. We cite it for the lesson only; consult the provider’s own post-incident writing for specifics rather than relying on our summary of figures.

Operationally, add response rate limiting on any authoritative server you run yourself, since DNS over UDP is a classic reflection vector, and keep response sizes small so your servers are poor amplifiers.

Trade-offs, Gotchas, and What Goes Wrong

DNS incidents are rarely caused by DNS being slow. They are caused by DNS being consistently wrong in a way that every cache faithfully preserves. The most useful way to review a design is to ask how each component fails and who notices.

Failure modes diagram showing dual-provider authoritative DNS, shared health signal and automation writing records, with blast radius paths

Figure 4: Failure modes. Redundant providers protect against provider outage but not against a bad record pushed by shared automation or a shared health signal.

Automation that writes DNS is a single point of failure. AWS’s published summary of its October 19 to 20, 2025 us-east-1 event attributes the regional DynamoDB endpoint failure to a latent race condition in its DNS management system. Per that summary, a delayed automation component applied an older plan over a newer one, and cleanup then deleted the older plan, leaving an empty record for the endpoint’s name. DNS failures began at 11:48 PM PDT on October 19, all DNS information was restored at 2:25 AM, and cached records expired at about 2:40 AM on October 20. Note the last interval: even after the authoritative data was fixed, recovery was gated by cache expiry. The lesson generalizes. Put safeguards in front of any process that can write an empty or drastically smaller record set: reject plans that remove every address, and require a freshness check at the moment of write, not at the start of the job.

Anycast withdrawal logic can remove everything. The Meta outage of October 2021, as described in the company’s engineering post, is the canonical example. A health rule that withdraws a node’s BGP route when it loses backbone connectivity is correct for one node and catastrophic when the cause is shared. Add a floor: never withdraw more than a set fraction of sites on the basis of a health signal that could be common-mode.

Split brain between providers. Running two authoritative providers for resilience means two copies of the zone. If one is updated and the other is not, resolvers get different answers depending on which name server they ask, and the divergence is invisible in each provider’s console. Use a single source of truth (an infrastructure-as-code repository), push to both providers from one pipeline, and run a continuous check that queries each provider’s name servers directly and compares answers. Dynamic steering features are provider-specific and do not synchronize, so with multiple providers you generally give up GSLB-style policies on the secondary or accept simpler, static records there.

Health checks that agree too much. If every region’s health depends on one shared database, a database fault turns every region unhealthy, and fail-open behavior returns all of them, which is the best case. If a provider instead returns nothing, clients get NODATA and your site is down because DNS was trying to help. Test the “all endpoints unhealthy” path deliberately.

Negative caching surprises. Pre-creating a name that is later referenced avoids cached NXDOMAINs. In a blue-green migration where the new environment’s name did not exist when the first clients probed it, expect a negative-caching tail equal to the SOA negative TTL.

ECS cache fragmentation and privacy. Enabling client subnet on your authoritative answers makes resolvers cache per-scope entries, which increases your query volume and reduces cache hit rates. If your steering granularity is only three regions, answering with a broad scope (or scope /0 for non-geographic names) keeps caches efficient.

Stale-serving surprises. Resolvers that implement serve-stale will keep answering with old data during an authoritative outage. That is good for availability and bad if the old data points at an endpoint you decommissioned. Do not release IP addresses back to a pool or terminate old endpoints until well after the record’s TTL plus stale-retention allowances, and keep a redirect or a clear error on the old address for a long time.

Practical Recommendations

Start by deciding what each layer is for, then test the path you rely on in an incident. Use anycast DNS from a provider with a large footprint, and put your zone on two independent providers if the business cannot tolerate a provider-level outage; accept the synchronization cost with automation and continuous comparison. Use GeoDNS or latency routing for performance placement, not for compliance, and ensure every region can carry the load of its failed neighbor, because failover that lands on an undersized region converts an outage into a brownout.

Set TTLs by record class rather than globally, and lower them ahead of planned changes. Expect a straggler tail and design the application to tolerate old destinations: keep the old endpoint serving or redirecting, and use connection draining. For failure detection that must be fast, put it in the load balancer or an anycast front door, and treat DNS as a coarse second line. When DNS-based failover is required, probe a regional deep health endpoint that avoids global shared dependencies, and test the all-unhealthy case.

Checklist:

  • Publish at least four name servers across two networks; verify with dig NS and direct queries.
  • Classify records and assign TTLs by change frequency; document the advance-lowering procedure.
  • Compute your failover SLO with the tail included; measure client behavior from real traffic logs.
  • Guard automation: refuse empty or sharply reduced record sets and re-check freshness at write time.
  • Cap BGP withdrawal by health signal so common-mode faults cannot withdraw every site.
  • Compare answers across providers continuously and alert on divergence.
  • Rehearse the failover quarterly in a game day and record time to 50, 90 and 99 percent of traffic moved.
  • If using DNSSEC, rehearse key rollovers in staging and automate DS updates.

If your devices are constrained, add one more item: long-lived embedded clients often resolve once at boot and cache indefinitely, so they need an explicit re-resolution policy and a secure channel. Our analysis of TLS 1.3 on constrained IoT devices covers the reconnection costs that interact with this. And if the thing behind your DNS records is a database, remember that moving traffic is the easy half; the data-consistency half is treated in multi-region active-active database architecture.

Frequently Asked Questions

What is anycast DNS and how is it different from GeoDNS?

Anycast DNS announces the same IP address from many sites using BGP, so each query reaches a nearby server; it is about keeping the DNS service available and fast. GeoDNS is about the content of the answer: returning different records to different clients based on location or latency. They work together. Anycast gets the query to a nearby authoritative server, and GeoDNS logic on that server picks which regional endpoint to return. One is routing of queries, the other is routing of users.

Does a lower TTL make DNS failover faster?

Only partly. TTL bounds how long compliant caches hold an old answer, but total failover time also includes health-check detection and control-plane delay, and some resolvers, runtimes and connection pools ignore or exceed the TTL. In the worked example above, cutting TTL from 60 to 30 seconds shaved roughly 15 seconds off the median and did nothing for stragglers. Lower TTLs also raise query cost and load, so use short TTLs only on records that GSLB rewrites.

What is EDNS Client Subnet and should I enable it?

EDNS Client Subnet, defined in RFC 7871, lets a recursive resolver pass a truncated client prefix (recommended /24 for IPv4 and /56 for IPv6) to the authoritative server so geolocation reflects the user, not the resolver. It improves steering accuracy for users on distant public resolvers, but it exposes part of the client address and enlarges resolver caches. The RFC recommends leaving it off by default. Enable it when steering accuracy has measurable benefit.

How long do negative DNS answers stay cached?

Under RFC 2308, the negative TTL is the lesser of the SOA record’s own TTL and its MINIMUM field, and resolvers should cap negative caching, with one to three hours suggested as workable. Resolution failures such as SERVFAIL are cached separately; RFC 9520 requires at least 1 second and at most 300 seconds. So a missing name can stay missing for hours, while a transient server failure usually clears within five minutes.

Should I use two DNS providers?

Use two if a provider-wide outage is an unacceptable risk, because a single provider’s DNS failure takes down every name you host there, as the 2016 Dyn attack demonstrated. The cost is complexity: records must stay synchronized, provider-specific steering features do not carry across, and you need continuous cross-provider comparison. Drive both from one infrastructure-as-code pipeline, and accept simpler static records on the secondary.

Can DNS-based GSLB replace a load balancer?

No. DNS returns an address once per TTL per resolver, with no knowledge of request load, connection state or per-request health. A load balancer reacts in milliseconds and sees every request. DNS GSLB is appropriate for choosing a region or site; a load balancer chooses a backend within it. Using DNS alone for backend selection leads to uneven load because caches and large resolvers concentrate many users behind one answer.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *