Multi-region failover
Floating IPs are region-scoped — they can't move between regions. For cross-region failover (whole-region outage, regional latency degradation), the right primitive is DNS-level routing with health checks. Latency-based steering routes each user to their nearest healthy region; on a regional failure, the DNS layer takes that region out of rotation.
Target RTO with this setup: 20–60 seconds depending on TTL and how quickly your DNS resolvers re-query.
What you'll set up
your customers
│
app.example.com ← DNS A record set
│
┌─────────────┴─────────────┐
↓ ↓
us-east-1 eu-central-1
(203.0.113.5/32) (198.51.100.5/32)
VM-A VM-B
│ │
└─ replicated DB ───────────┘
(logical replication)
DNS resolves to whichever region is closer to the resolver. If a region fails its health check, DNS stops returning that region's IP until it recovers.
Prereqs
- Two VMs in different regions, each behind a floating IP.
- Your application running on both VMs, ready to serve traffic.
- A database or stateful store that exists in both regions, with replication between them. For Postgres, migrate-from-RDS-style logical replication is the same primitive. For Redis, mirror cache misses; for object storage, use cross-region replication.
- A DNS zone hosted on NimbusNexus DNS (or any DNS provider that supports health-checked record sets — Route53, Cloudflare, etc.).
- A health-check endpoint that genuinely verifies your application can serve traffic (not just
200 OKon/healthz).
export NIMBUS_KEY="nn_live_xxxxxxxxxxxxxxxx"
export ZONE_ID="zone_01HG7..."
export EAST_IP_ID="ip_01HG7Y3..." # floating IP in us-east-1
export WEST_IP_ID="ip_01HG7Y4..." # floating IP in eu-central-1
1 · Create the multi-value record set
Add an A record set with both regions' IPs, latency-routed:
EAST_IPV4=$(curl -s https://api.nimbusnexus.net/v1/floating-ips/$EAST_IP_ID \
-H "Authorization: Bearer $NIMBUS_KEY" | jq -r '.ipv4')
WEST_IPV4=$(curl -s https://api.nimbusnexus.net/v1/floating-ips/$WEST_IP_ID \
-H "Authorization: Bearer $NIMBUS_KEY" | jq -r '.ipv4')
curl -X POST https://api.nimbusnexus.net/v1/dns/zones/$ZONE_ID/records \
-H "Authorization: Bearer $NIMBUS_KEY" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"app.example.com\",
\"type\": \"A\",
\"routing_policy\": \"latency\",
\"ttl\": 60,
\"values\": [
{ \"value\": \"$EAST_IPV4\", \"region\": \"us-east-1\" },
{ \"value\": \"$WEST_IPV4\", \"region\": \"eu-central-1\" }
]
}"
Key fields:
routing_policy: 'latency'— DNS resolvers receive whichever IP corresponds to the geographically closest region. Alternatives:'weighted'(percentage split),'failover'(primary/secondary, never round-robin),'geo'(route by client country).ttl: 60— 60-second TTL. Lower TTL = faster failover (resolvers re-query sooner) at the cost of more DNS queries. For a service with high availability needs, 60 is right; for lower-SLA setups, 300 is fine.
2 · Add health checks
Each value in the record set gets a health check. Failed checks remove that value from rotation:
curl -X POST https://api.nimbusnexus.net/v1/dns/zones/$ZONE_ID/records/$RECORD_ID/health-checks \
-H "Authorization: Bearer $NIMBUS_KEY" \
-H "Content-Type: application/json" \
-d '{
"value_index": 0,
"type": "https",
"path": "/healthz",
"expected_status": 200,
"interval_seconds": 10,
"failure_threshold": 3,
"success_threshold": 2
}'
Repeat for value_index: 1 (the second region). What each field does:
interval_seconds: 10— check every 10 seconds.failure_threshold: 3— three consecutive failures before considering the value down. Combined withinterval_seconds, that's 30 seconds before a region drops out — slightly above the network-flake threshold, well below "we're noticing in PagerDuty already."success_threshold: 2— two consecutive successes before considering the value back up. Prevents thrashing during a flaky recovery.
The health-check endpoint matters. Don't make it return 200 OK unconditionally — that defeats the point. Make it actually check downstream dependencies (database connection, cache, the queue) so it fails when the application would fail.
3 · Test it
Force a "failure" by blocking the health-check probe on one region:
# On the us-east-1 VM, drop traffic from the health-check source CIDR
sudo iptables -A INPUT -s <healthcheck-cidr> -j DROP
(Get the health-check source CIDRs from GET /v1/dns/healthcheck-source-cidrs.)
Within 30 seconds, the record set should stop returning the us-east-1 IP:
dig +short app.example.com
# Should return only the eu-central-1 IP for the next several seconds
Reverse the iptables rule to restore the region. Within 20 seconds (2 * interval_seconds), it should return to rotation.
4 · Database replication
The DNS layer handles where traffic lands; the data layer is harder. The patterns:
For read-mostly workloads:
- Primary DB in one region, read-replica in the other.
- App reads from the local region (replica when in the secondary region).
- Writes always go to the primary (so the secondary region has a higher write latency).
- On primary-region failure: promote the replica to primary, fail over reads + writes to the new primary.
For write-heavy workloads with multi-master tolerance:
- Use a database that supports active-active replication (CockroachDB, YugabyteDB).
- Both regions read + write locally; conflicts resolved by the engine.
- More complex to operate; only justified when single-master write latency is a real problem.
For cache and queue:
- Cache: assume cold cache after failover. Most apps recover within minutes; the SLA hit is tolerable.
- Queue: keep queues per-region; the application accepts that a regional queue may be unavailable until the region recovers.
NimbusNexus doesn't yet ship a multi-region active-active database product. For now, manage replication yourself via logical replication (Postgres), binlog (MySQL), or change streams (MongoDB).
What this isn't
- Not active-active failover for state: writes still concentrate on one primary. The DNS layer steers users; the database tier is still single-master most of the time.
- Not transparent to clients: TCP connections to the failed region's IP get reset; clients reconnect (most HTTP clients handle this fine; some long-polling or SSE clients need a retry handler).
- Not zero RTO: 20–60 seconds is the floor at TTL = 60. Lower TTL helps but at the cost of more DNS query volume.
Next steps
- DNS zones — the zone API the record set lives in.
- DNS records — every routing policy + the field semantics.
- Floating IPs — the within-region failover primitive this builds on.
- Blue/green deploys — same idea, within a region.