Skip to content
JavaAgentic

Type at least two characters. Try “RAG”, “pgvector” or “tool calling”.

Disaster Recovery & High Availability

Planning for failure: defining RPO and RTO honestly, replication trade-offs, multi-region topologies and their costs, DNS failover, and testing recovery before you need it.

Advanced5 min readUpdated
On this page

Disaster recovery planning fails in a predictable way: a document is written, nobody tests it, and the first real execution discovers that the backup restore takes six hours and the runbook references a tool that was decommissioned.

Key Takeaways

  • RPO is acceptable data loss; RTO is acceptable downtime. Both are business decisions.
  • Synchronous replication gives RPO zero and costs write latency.
  • Multi-AZ handles most real failures; multi-region is a much bigger commitment.
  • An untested backup means an unknown RTO, which means no plan.
  • Chaos engineering turns your assumptions into evidence.

RPO and RTO

RPO looks backwards from the failure — how much data can be lost. RTO looks forwards — how long recovery may take.
TargetWhat it requiresRoughly
RPO 24h / RTO 24hNightly backup, restore by handBaseline
RPO 1h / RTO 4hHourly snapshots, documented runbook1.2x
RPO 5m / RTO 1hAsync replica, warm standby1.5-2x
RPO 0 / RTO 5mSynchronous replication, automated failover2-3x
RPO 0 / RTO 0Active-active, global routing3x+ and substantial complexity

State these as numbers agreed with the business, not as aspirations. "As little downtime as possible" is not a target — it cannot be designed against or tested. "RTO one hour" tells you whether a manual restore is acceptable.

Replication trade-offs

Asynchronous — the primary commits, replication follows. No write latency cost, RPO of seconds to minutes depending on lag. The default for most systems, and correct for most.

Synchronous — the transaction does not commit until a standby has it. RPO zero, at the cost of a network round trip on every write. Across availability zones that is single-digit milliseconds; across regions it is tens, which changes your application's performance profile substantially.

Semi-synchronous — wait for at least one standby, not all. A reasonable middle ground: bounded data loss with less latency cost than full synchronous.

Whichever you choose, monitor replication lag as a first-class metric. Lag is your actual RPO, and it is not the configured value — it is whatever the replica is currently behind by. A replica that has silently fallen an hour behind means an hour of data loss on failover, regardless of what the design document says.

Multi-region topologies

PatternStandby stateRTOCost
Backup and restoreNothing runningHoursLowest
Pilot lightData replicated, minimal compute10-60 minLow
Warm standbyScaled-down full stack5-15 minMedium
Active-activeBoth serving trafficNear zeroHighest

Active-active is the one to be cautious about. Bidirectional data synchronisation means conflict resolution, and conflict resolution means deciding what happens when the same record is written in two regions within the replication window. For a read-heavy workload it is manageable; for anything with contended writes it is a genuinely hard distributed systems problem.

Warm standby is the sweet spot for most systems that need cross-region recovery: a scaled-down copy that is continuously receiving replicated data and can be scaled up in minutes.

Failover

route53-failover.tf
resource "aws_route53_health_check" "primary" {
  fqdn              = "api-eu.acme.com"
  type              = "HTTPS"
  resource_path     = "/actuator/health/readiness"
  failure_threshold = 3
  request_interval  = 30
}
 
resource "aws_route53_record" "primary" {
  zone_id        = var.zone_id
  name           = "api.acme.com"
  type           = "A"
  set_identifier = "primary"
  failover_routing_policy { type = "PRIMARY" }
  health_check_id = aws_route53_health_check.primary.id
  alias { name = aws_lb.eu.dns_name, zone_id = aws_lb.eu.zone_id, evaluate_target_health = true }
}

DNS failover has a floor set by TTL plus client caching, and clients routinely ignore TTL. A 60-second TTL does not give a 60-second RTO — some clients will keep resolving the old address for far longer. Where the RTO must be tight, a global load balancer with anycast is a better mechanism than DNS.

The health check path matters. Pointing it at / returns healthy while the database is unreachable; pointing it at readiness means the check reflects the service's actual ability to serve.

Testing the plan

An untested plan is a hypothesis. Three levels of testing, in increasing order of confidence.

Tabletop. Walk through the runbook in a meeting. Cheap, and it reliably finds missing steps, stale tool references and unclear ownership. Do this quarterly.

Restore drill. Actually restore a backup into an isolated environment and time it. This produces your real RTO number, which is frequently several times the assumed one. Do this quarterly and record the timing.

Full failover. Fail over to the standby region for real, serve production traffic from it, and fail back. This is the only test that proves the plan, and the only one that finds the dependency nobody documented — a certificate, a queue, a third-party allowlist that only knows the primary region's addresses. Do this annually, announced, in a low-traffic window.

Chaos engineering

litmus-experiment.yaml
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata: { name: order-service-chaos }
spec:
  appinfo:
    appns: production
    applabel: 'app=order-service'
  experiments:
    - name: pod-delete
      spec:
        components:
          env:
            - { name: TOTAL_CHAOS_DURATION, value: '60' }
            - { name: CHAOS_INTERVAL, value: '10' }
            - { name: PODS_AFFECTED_PERC, value: '50' }

The method matters more than the tool. Define the steady state you expect — error rate under 0.1%, p99 under 500ms. State a hypothesis: killing half the pods will not breach it. Run the experiment in a controlled window with a clear abort condition. Then either your hypothesis held, or you have learned something specific and actionable.

Start small and in staging. Kill one pod, not a zone. Once the team is comfortable and the obvious gaps are fixed, run in production during business hours when everyone is awake — which is exactly the opposite of when a real failure will happen, and precisely the point.

What to take away

Agree RPO and RTO as numbers with the business, then buy the cheapest architecture that meets them — which for most systems is multi-AZ, not multi-region. Monitor replication lag, because it is your real RPO. Test restores quarterly and time them, fail over annually, and use chaos experiments to convert assumptions about resilience into evidence.

Frequently Asked Questions

What RPO and RTO should I target?
The ones the business will pay for. RPO zero requires synchronous replication, which adds latency to every write; RTO near zero requires active-active, which multiplies cost and complexity. Ask what an hour of downtime and five minutes of lost data actually cost, then buy the cheapest option that meets it.
Is a backup a disaster recovery plan?
Only if you have restored from it recently and timed the restore. An untested backup has an unknown restore time and an unknown success probability, which means your RTO is unknown. Schedule a restore drill quarterly and record how long it took.
Do I need multi-region?
Most systems do not. Multi-AZ within one region survives the overwhelming majority of real failures at a fraction of the cost and complexity. Multi-region is for genuine regulatory requirements, global latency needs, or a business where a regional outage is existential.

Related tutorials