
What is DNS failover and how does it keep sites online?
DNS failover is a setup where your DNS provider continuously checks whether your main server is healthy and, if it stops responding, automatically changes the DNS answer for your domain to point at a backup server. Visitors who look up your site after the switch get the backup's IP address instead of the failed one, so the site stays reachable while you fix the problem. When the primary recovers, the provider switches the answer back.
It's one of the most affordable ways to add resilience, because it uses infrastructure you already have and doesn't need a load balancer in front of everything. It isn't instant, though, and how quickly it works depends on TTLs, caching and how your health checks are set up. In this article you'll learn how DNS failover works step by step, what happens during the switch, the common designs, how to configure it with Amazon Route 53 and with your own script, and the pitfalls that catch people out.
How DNS Failover Works
DNS failover has three moving parts: health checks, a decision rule, and the DNS answer itself.
1. Health Checks
The DNS provider, or a monitoring service working with it, sends regular probes to your endpoints. A probe might be:
- An HTTP or HTTPS request to a path such as
/health, expecting a200status code and sometimes a particular string in the response body. - A TCP connection to a port, such as 443 for a website or 25 for a mail server.
- An ICMP ping, which only proves the machine is reachable, not that your application works.
Good providers run checks from several locations around the world and only mark an endpoint unhealthy when a number of locations agree. That prevents a single network glitch near one checker from triggering a failover.
2. The Decision Rule
You choose how many consecutive failures count as "down", and how many successes count as "recovered". For example, three failures at 30-second intervals means it takes about 90 seconds to detect an outage. Faster intervals detect problems sooner but cost more and increase the risk of flapping, where the record switches back and forth on a marginal service.
3. Changing the Answer
When the primary is marked unhealthy, the provider stops returning its address and starts returning the secondary's. Depending on the provider, this happens in one of two ways:
- Dynamic answers: The authoritative server decides at query time which record to return, based on the latest health status. This is how Route 53, Cloudflare load balancing and NS1 work.
- Record updates: A monitoring system calls an API to rewrite the record. This is how most do-it-yourself setups work.
Either way, from the outside it looks like the record has changed.
What Happens During a Failover
Here's a realistic timeline for a site with a TTL of 60 seconds and health checks every 30 seconds with a failure threshold of three:
| Time | Event |
|---|---|
| 0:00 | Primary server crashes |
| 0:00 to 1:30 | Health checks fail three times in a row |
| about 1:30 | Provider marks the primary unhealthy and starts returning the backup address |
| 1:30 to 2:30 | Resolvers with a cached answer keep returning the old address until the 60-second TTL runs out |
| about 2:30 | Most new visitors are reaching the backup |
| Later | A small number of clients with stubborn caches or open connections keep trying the old address |
So even a well-tuned DNS failover usually takes a couple of minutes to take full effect. That's far better than hours of downtime while someone wakes up and changes a record by hand, but it isn't seamless.
Why TTL Matters So Much
When a resolver caches a record, it keeps using it until the TTL expires. If your record has a TTL of 3600 seconds, some users could be stuck on the failed server for up to an hour after the switch. For records that use failover, keep TTLs low, typically 30 to 120 seconds.
Lower TTLs mean more queries to your DNS provider. With providers that charge per query, that's a small cost increase. With providers that don't, it's free resilience.
Client-Side Caching
Resolvers aren't the only caches. Operating systems cache DNS answers, browsers have their own caches, and applications such as Java services may cache lookups according to their own settings. Long-lived connections, such as HTTP/2 connections a browser keeps open, don't trigger new lookups at all. A user who already has your site open may need to refresh, or even wait for their browser to drop the connection, before they reach the backup.
Common DNS Failover Designs
Active-Passive
The primary serves all traffic. The secondary sits idle, or serves a reduced version of the site, until the primary fails. This is the classic setup and the easiest to reason about.
A popular variation is to fail over to a static maintenance page hosted somewhere completely separate, such as object storage or a static hosting platform. Visitors see a friendly "we'll be back shortly" page rather than a browser error, and it costs almost nothing to keep ready.
Active-Active
Several servers serve traffic at the same time, usually through multiple records or weighted routing. Health checks remove any that fail, and the remaining ones absorb the load. This makes better use of your servers but requires each one to be able to handle extra traffic when others are down.
Multi-Region and Multi-Provider
For larger sites, the primary and secondary are in different regions or even with different cloud providers. DNS failover is one of the few tools that works naturally across providers, because DNS doesn't care where an IP address lives.
Your DNS itself is also a dependency. If your DNS provider has an outage, failover can't help. Some organisations use two DNS providers to cover that risk, which is a separate decision from failover.
Setting Up Failover in Amazon Route 53
Route 53 has a dedicated failover routing policy. You create a health check for the primary, then two records with the same name: one marked PRIMARY with the health check attached, and one marked SECONDARY.
Step 1: Create a Health Check
aws route53 create-health-check \
--caller-reference "www-primary-$(date +%s)" \
--health-check-config '{
"Type": "HTTPS",
"FullyQualifiedDomainName": "origin1.example.com",
"Port": 443,
"ResourcePath": "/health",
"RequestInterval": 30,
"FailureThreshold": 3
}'
The command returns a JSON document containing the health check's Id. Note it down, because the primary record needs it.
Step 2: Create the Primary and Secondary Records
Save this as failover.json, replacing the health check ID with your own:
{
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"SetIdentifier": "primary",
"Failover": "PRIMARY",
"TTL": 60,
"HealthCheckId": "abcdef12-3456-7890-abcd-ef1234567890",
"ResourceRecords": [{ "Value": "192.0.2.10" }]
}
},
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"SetIdentifier": "secondary",
"Failover": "SECONDARY",
"TTL": 60,
"ResourceRecords": [{ "Value": "198.51.100.10" }]
}
}
]
}
Then apply it:
aws route53 change-resource-record-sets \
--hosted-zone-id Z0123456789EXAMPLE \
--change-batch file://failover.json
Route 53 returns the primary while its health check passes and the secondary when it fails. You can attach a health check to the secondary as well. If both are unhealthy, Route 53 returns the primary anyway, on the basis that some answer is better than none.
The Same Setup in Terraform
resource "aws_route53_health_check" "primary" {
fqdn = "origin1.example.com"
port = 443
type = "HTTPS"
resource_path = "/health"
request_interval = 30
failure_threshold = 3
}
resource "aws_route53_record" "www_primary" {
zone_id = var.zone_id
name = "www.example.com"
type = "A"
ttl = 60
records = ["192.0.2.10"]
set_identifier = "primary"
health_check_id = aws_route53_health_check.primary.id
failover_routing_policy {
type = "PRIMARY"
}
}
resource "aws_route53_record" "www_secondary" {
zone_id = var.zone_id
name = "www.example.com"
type = "A"
ttl = 60
records = ["198.51.100.10"]
set_identifier = "secondary"
failover_routing_policy {
type = "SECONDARY"
}
}
Other managed providers offer the same idea under different names. Cloudflare's load balancing uses pools and monitors, NS1 uses filter chains with monitoring, and Azure Traffic Manager uses a priority routing method. Check whether failover is included in your plan or sold as an add-on.
A Do-It-Yourself Failover Script
If your DNS provider has an API but no built-in health checks, you can build basic failover yourself. This Bash script checks the primary and updates a Cloudflare DNS record when its health changes. It needs curl and jq, an API token with DNS edit permission, and the zone and record IDs:
#!/usr/bin/env bash
set -euo pipefail
CF_API_TOKEN="your-api-token"
ZONE_ID="your-zone-id"
RECORD_ID="your-record-id"
RECORD_NAME="www.example.com"
PRIMARY_IP="192.0.2.10"
BACKUP_IP="198.51.100.10"
# Check the primary directly by IP, sending the right Host header
if curl -fsS --max-time 5 --resolve "www.example.com:443:${PRIMARY_IP}" \
"https://www.example.com/health" > /dev/null; then
TARGET="$PRIMARY_IP"
else
TARGET="$BACKUP_IP"
fi
CURRENT=$(curl -fsS \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
"https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
| jq -r '.result.content')
if [ "$CURRENT" != "$TARGET" ]; then
echo "Switching ${RECORD_NAME} from ${CURRENT} to ${TARGET}"
curl -fsS -X PATCH \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "{\"content\":\"${TARGET}\",\"ttl\":60}" \
"https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
> /dev/null
fi
Run it every minute from cron on a machine that isn't in the same place as the primary:
* * * * * /usr/local/bin/dns-failover.sh >> /var/log/dns-failover.log 2>&1
This works, but it has the weaknesses you'd expect: a single checker, a single location, and no protection against flapping. Add a failure counter stored in a file, check from at least two places, and alert yourself whenever it switches. For anything important, a provider's built-in failover is more reliable.
Testing and Verifying Failover
Never assume failover works. Test it before you need it.
-
Check the current answer at the authoritative server and at a public resolver:
dig @ns-123.awsdns-15.com www.example.com A +short dig @1.1.1.1 www.example.com A +noall +answerThe second command shows the remaining TTL in the answer, so you can see how long resolvers will hold the current address:
www.example.com. 47 IN A 192.0.2.10 -
Simulate a failure, for example by making the health endpoint return a
503, or by blocking the health checkers at the firewall on a test hostname. -
Watch the answer change at the authoritative server, then at public resolvers once the TTL has expired.
-
Load the site through the backup and confirm it actually works, including TLS certificates, database access and anything else it depends on.
-
Restore the primary and confirm traffic moves back.
Repeat the test periodically. Backups that haven't been tested for a year often fail for boring reasons, such as an expired certificate or an outdated copy of the code.
Pitfalls to Avoid
- Long TTLs on failover records: A 24-hour TTL makes failover pointless. Keep it short.
- Health checks that are too shallow: A ping or a static page might succeed while your database is down. Make
/healthcheck the things that matter, without being so strict that a minor dependency causes a failover. - Health checks that are too strict: If a slow third-party API makes your health check fail, you might fail over for no reason. Be deliberate about what counts as "down".
- A backup that can't cope: If the secondary has a fraction of the primary's capacity, it may collapse under full traffic.
- Data that isn't replicated: Failing over to a server with yesterday's database can be worse than staying down. Decide in advance how data is kept in sync, and whether the backup should be read-only.
- Firewalls blocking health checkers: Providers publish the IP ranges their checkers use. Allow them, or your endpoints will look permanently down.
- Forgetting other records: Failing over
wwwdoesn't help if your API orMXrecords still point at the failed site. - Silent failovers: Set up alerts so you know when a switch has happened. Otherwise you may run on the backup for days without realising.
FAQ: DNS Failover
Detection usually takes 30 seconds to a few minutes, depending on your check interval and failure threshold. After that, users switch as cached answers expire, so total time is roughly detection time plus the record's TTL.
Between 30 and 120 seconds is common. Very low TTLs speed up failover but increase query volume, and some resolvers apply their own minimums, so going below 30 seconds rarely helps much.
No, although they often work together. Load balancing spreads traffic across several healthy servers, while failover moves traffic away from a server that has failed. Many providers combine both, removing unhealthy servers from a balanced set.
Some will. Users with cached answers or open connections may see errors until their cache expires or their browser makes a new connection. A refresh usually fixes it once the TTL has passed.
No. Failover relies on your DNS provider answering queries. To protect against provider outages, use more than one DNS provider, which is a separate design decision.
Yes, and it's a popular low-cost option. Host a simple status page on separate infrastructure, such as object storage or a static hosting service, and use it as the secondary target.
Email already has a form of failover built in. MX records have priorities, and sending servers try lower-priority servers when the preferred one is unavailable, then retry later. DNS failover is more useful for websites and APIs.
Conclusion
DNS failover keeps sites online by watching your servers and switching the DNS answer to a backup when the primary stops responding. Health checks detect the problem, the provider changes which address it returns, and new visitors are sent to the working server as cached answers expire. It's simple, inexpensive and works across regions and providers.
Its effectiveness depends on the details: short TTLs, health checks that reflect real application health, a backup that can genuinely handle the traffic, and regular testing. Set those up carefully, add alerts so you know when it triggers, and DNS failover turns many would-be outages into a couple of minutes of disruption.


