Definition
- RTO (Recovery Time Objective) – the maximum acceptable downtime after a disaster.
- RPO (Recovery Point Objective) – the maximum acceptable data loss, measured in time.
- Backup and Restore – cheapest pattern; backups in the recovery region, nothing running; hours of RTO.
- Pilot Light – database continuously replicating, compute off; moderate cost; faster RTO.
- Warm Standby – scaled-down but fully functional copy always running; minutes of RTO.
- Multi-Site Active/Active – full production in multiple regions; near-zero RTO and RPO; highest cost.
- Testing – regular game-days where the team performs real failover, measures actual RTO/RPO, and fixes gaps.
Imagine National Bank of India's main branch burns down. Backup and Restore is keeping ledgers in a warehouse across town: safe, but it takes a day to rent a new building. Pilot Light is a tiny skeleton branch with just the vault running, ready to staff up fast. Warm Standby is a smaller working branch always open. Multi-Site Active/Active is two equal branches at once. And a DR plan never rehearsed is like a fire drill written on paper but never practised: on the real day, people run to locked doors.
Understanding the Concepts
What are RTO and RPO?
Everything in disaster recovery flows from two business targets. RTO is the clock: how long can the system be down? RPO is the data: how much data can you afford to lose? These are business decisions, not technical ones.
package com.bank.dr;
import java.time.Duration;
public class RecoveryObjectives {
private final String service;
private final Duration rto;
private final Duration rpo;
public RecoveryObjectives(String service, Duration rto, Duration rpo) {
this.service = service;
this.rto = rto;
this.rpo = rpo;
}
public static void main(String[] args) {
// Core payments: near-zero downtime, near-zero data loss
var payments = new RecoveryObjectives("payments-ledger",
Duration.ofMinutes(5), Duration.ofSeconds(0));
// Monthly reports: can tolerate a day of downtime
var reports = new RecoveryObjectives("monthly-reports",
Duration.ofHours(24), Duration.ofHours(1));
System.out.println(payments.service + " RTO: " + payments.rto);
System.out.println(reports.service + " RTO: " + reports.rto);
}
}
What are the four DR patterns?
The four patterns trade cost for speed along a continuum.
| Pattern | RTO | RPO | Steady-State Cost | Best For |
|---------|-----|-----|-------------------|----------|
| Backup and Restore | Hours | Backup frequency | Very low (storage only) | Non-critical, tolerant of downtime |
| Pilot Light | 10-30 min | Near-zero (replication) | Moderate (DB replica) | Important but not real-time |
| Warm Standby | Minutes | Near-zero | Higher (full stack, small) | Fast recovery needed |
| Multi-Site Active/Active | Near-zero | Near-zero | Highest (2x capacity) | Mission-critical, zero downtime |
How does Backup and Restore work?
You take regular backups and copy them to the recovery region. Nothing runs there until disaster strikes. Recovery means provisioning infrastructure from scratch and restoring from backup.
# Nightly backup to recovery region
aws rds create-db-snapshot --db-instance-identifier bank-accounts-db \
--db-snapshot-identifier bank-accounts-$(date +%Y%m%d)
# Copy snapshot to recovery region
aws rds copy-db-snapshot \
--source-db-snapshot-identifier arn:aws:rds:ap-south-1:123:snapshot:bank-accounts-20240315 \
--target-db-snapshot-identifier bank-accounts-20240315-dr \
--source-region ap-south-1 \
--region ap-southeast-1
# Terraform for recovery -- only applied during a disaster
resource "aws_db_instance" "bank_accounts_dr" {
snapshot_identifier = "bank-accounts-20240315-dr"
instance_class = "db.r6g.xlarge"
# RTO: hours (provision + restore + DNS cutover)
# RPO: up to 24 hours (nightly backup frequency)
}
How does Pilot Light work?
The core (database) replicates continuously. Compute is off. On disaster, you start and scale the application tier against the already-current data.
# Pilot Light: database always replicating, compute off
# Primary region (ap-south-1):
resource "aws_db_instance" "bank_accounts" {
instance_class = "db.r6g.2xlarge"
# Full production database
}
# Recovery region (ap-southeast-1):
resource "aws_db_instance" "bank_accounts_replica" {
replicate_source_db = aws_db_instance.bank_accounts.arn
instance_class = "db.r6g.xlarge" # Can be smaller
# Continuously replicating -- RPO near zero
}
# Compute: AMIs ready, auto-scaling groups at 0 instances
# On failover: promote replica, scale ASG to desired count
How does Warm Standby work?
A scaled-down but fully functional copy runs all the time. Failover is redirecting traffic and scaling up.
# Warm Standby: everything running, just smaller
# Primary: 5 replicas, full database
# Standby: 2 replicas, read replica, smaller instances
# Failover steps:
# 1. Promote read replica to primary (seconds)
# 2. Scale standby replicas from 2 to 5 (minutes)
# 3. Update DNS to point to standby region
# Total RTO: minutes
How does Multi-Site Active/Active work?
Full production capacity runs in two or more regions simultaneously. If one region dies, traffic shifts to the survivors, often automatically.
Active/Active Architecture:
┌──────────────────────────┐
│ Global Load Balancer │
└──────┬───────────┬───────┘
│ │
┌────────▼──────┐ ┌──▼────────────┐
│ Region A │ │ Region B │
│ (ap-south-1) │ │ (ap-south-2) │
│ Full prod │ │ Full prod │
│ capacity │ │ capacity │
└────────┬──────┘ └──┬────────────┘
│ │
┌──────▼───────────▼──────┐
│ Bi-directional │
│ Data Replication │
│ (DynamoDB Global Table │
│ or Aurora Global DB) │
└─────────────────────────┘
Why must a DR plan be tested?
Documentation decays silently. The restore script references an old bucket. The failover depends on a DNS change nobody has permission to make. The replica has been failing for weeks. The runbook assumes a person who left.
# Common failures found ONLY during testing:
1. Backup restore script references old bucket name -- FAILS
2. DNS cutover requires a permission nobody has -- BLOCKED
3. Read replica secretly stopped replicating 3 weeks ago -- DATA LOSS
4. Runbook says "call Rajesh" but Rajesh left the company -- BLOCKED
5. Recovery region has no pre-provisioned VPC/subnets -- SLOW
# None of these show up in the document. They only surface during execution.
# Game-day checklist:
# 1. Announce the test window
# 2. Execute the failover exactly as documented
# 3. Measure actual RTO and RPO against targets
# 4. Record every gap and surprise
# 5. Fix all gaps before the next game-day
# 6. Repeat quarterly
How do you choose a pattern per workload?
package com.bank.dr;
import java.time.Duration;
public class DrPatternAdvisor {
public String recommend(Duration rto, Duration rpo, String criticality) {
if (rto.toMinutes() <= 5 && rpo.toSeconds() == 0) {
return "Multi-Site Active/Active -- near-zero RTO/RPO, highest cost";
}
if (rto.toMinutes() <= 30) {
return "Warm Standby -- minutes of RTO, high cost";
}
if (rpo.toMinutes() <= 15) {
return "Pilot Light -- moderate RTO, low RPO, moderate cost";
}
return "Backup and Restore -- hours of RTO, lowest cost";
}
public static void main(String[] args) {
var advisor = new DrPatternAdvisor();
System.out.println("Payments: " + advisor.recommend(
Duration.ofMinutes(5), Duration.ofSeconds(0), "tier-1"));
System.out.println("Statements: " + advisor.recommend(
Duration.ofMinutes(30), Duration.ofMinutes(5), "tier-2"));
System.out.println("Interest batch: " + advisor.recommend(
Duration.ofHours(24), Duration.ofHours(1), "tier-3"));
}
}
Payments: Multi-Site Active/Active -- near-zero RTO/RPO, highest cost
Statements: Warm Standby -- minutes of RTO, high cost
Interest batch: Backup and Restore -- hours of RTO, lowest cost
What are common mistakes with disaster recovery?
# WRONG: same DR pattern for every workload
"Everything gets active-active."
# Result: enormous cost for services that could tolerate hours of downtime
# CORRECT: match the pattern to the business criticality
Tier 1 (payments): Active/Active -- near-zero downtime justified
Tier 2 (statements): Warm Standby -- minutes acceptable
Tier 3 (batch): Backup and Restore -- hours acceptable
# WRONG: writing a DR plan and never testing it
"The document says RTO is 10 minutes."
# Actual RTO during first real incident: 4 hours (because of untested steps)
# CORRECT: quarterly game-days that measure actual RTO/RPO
Documented RTO: 10 minutes
Game-day measured RTO: 40 minutes (DNS step was manual)
Fix: automate DNS cutover
Next game-day measured RTO: 8 minutes (target met)
Visual Overview
DR Patterns: Cost vs. Recovery Speed
======================================
Cost ▲
│
│ ┌─────────────────────┐
│ │ Multi-Site │ RTO: ~0 RPO: ~0
│ │ Active/Active │
│ └─────────────────────┘
│ ┌─────────────────────┐
│ │ Warm Standby │ RTO: min RPO: ~0
│ └─────────────────────┘
│ ┌─────────────────────┐
│ │ Pilot Light │ RTO: 10-30m RPO: ~0
│ └─────────────────────┘
│ ┌─────────────────────┐
│ │ Backup and Restore │ RTO: hours RPO: backup freq
│ └─────────────────────┘
└──────────────────────────────▶ Recovery Speed
Slow Fast
Why Test?
==========
Document says: "RTO = 10 minutes"
│
First game-day
│
▼
Actual result: "RTO = 40 minutes"
- DNS step was manual (15 min delay)
- Replica had stopped syncing (data gap)
- Runbook referenced ex-employee
│
Fix the gaps
│
▼
Next game-day: "RTO = 8 minutes" (target met)
Key Takeaway
Disaster recovery is driven by two business targets: RTO (maximum downtime) and RPO (maximum data loss). The four patterns trade cost for speed: Backup and Restore is cheapest but takes hours, Pilot Light keeps the database replicating with compute off, Warm Standby runs a scaled-down full copy for minute-level recovery, and Multi-Site Active/Active runs full production in multiple regions for near-zero downtime.
The reason a plan must be tested is that documentation decays silently. The restore script references an old bucket, the failover depends on a permission nobody has, the replica has been secretly failing. None of this appears on paper. Regular game-days where the team performs real failover, measures actual RTO and RPO, and fixes every gap are the only way to know your plan actually works.
At National Bank of India I matched the nightly interest batch to Backup and Restore, the statements portal to Warm Standby, and the core payments ledger to active-active. The first game-day on the ledger took 40 minutes instead of the documented 10, because of a manual DNS step nobody had automated. That single test turned a comforting number into a concrete fix.
Remember one sentence: an untested DR plan is a hypothesis, and you never want to test it for the first time during a real outage.
---