Disaster Recovery Planning
RPO, RTO and runbooks.
Disaster Recovery Planning is a free SQL Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the SQL Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is Disaster Recovery?
Disaster Recovery (DR) is the set of policies, tools, and procedures designed to enable the recovery of vital technology infrastructure and systems after a natural or human-induced disaster.
In the context of databases, DR planning ensures that your data remains safe and your systems can be brought back online within acceptable time limits after events such as hardware failure, accidental data deletion, ransomware attacks, or datacenter outages.
Recovery Point Objective (RPO)
RPO defines the maximum acceptable amount of data loss measured in time. If your RPO is 1 hour, you must be able to recover data up to at least 1 hour before the disaster occurred.
A shorter RPO demands more frequent backups or continuous replication. You can query your backup history to verify you are meeting your RPO target.
-- Check the last backup time and calculate data loss window
SELECT
backup_id,
backup_type,
started_at,
finished_at,
EXTRACT(EPOCH FROM (NOW() - finished_at)) / 3600 AS hours_since_backup
FROM backup_log
WHERE status = 'SUCCESS'
ORDER BY finished_at DESC
LIMIT 5;Recovery Time Objective (RTO)
RTO defines the maximum acceptable length of time that your system can be offline after a disaster. If your RTO is 4 hours, your database must be fully operational within 4 hours of a failure.
RTO drives decisions about standby servers, failover automation, and restore procedures. Tracking restore durations over time helps you forecast whether you can meet your RTO.
-- Track restore durations to validate RTO compliance
SELECT
restore_id,
triggered_at,
completed_at,
EXTRACT(EPOCH FROM (completed_at - triggered_at)) / 60 AS restore_minutes,
CASE
WHEN EXTRACT(EPOCH FROM (completed_at - triggered_at)) / 3600 <= 4
THEN 'WITHIN RTO'
ELSE 'RTO BREACHED'
END AS rto_status
FROM restore_log
ORDER BY triggered_at DESC;RPO vs RTO — The Key Difference
These two metrics are often confused. Here is the simplest way to remember them:
- RPO = How much data can you afford to lose? (backward-looking, measured in time before disaster)
- RTO = How long can you afford to be down? (forward-looking, measured in time after disaster)
Together they define your recovery window and directly influence your backup frequency, replication strategy, and infrastructure budget.
-- Store RPO and RTO targets per database in a DR configuration table
CREATE TABLE dr_config (
db_name VARCHAR(100) PRIMARY KEY,
rpo_minutes INT NOT NULL,
rto_minutes INT NOT NULL,
tier VARCHAR(20) CHECK (tier IN ('CRITICAL', 'HIGH', 'MEDIUM', 'LOW')),
updated_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO dr_config (db_name, rpo_minutes, rto_minutes, tier) VALUES
('orders_db', 15, 60, 'CRITICAL'),
('analytics_db', 120, 240, 'MEDIUM'),
('archive_db', 480, 480, 'LOW');Types of Backups
There are three primary backup strategies, each balancing speed and storage:
- Full backup: A complete snapshot of the entire database. Slowest to create, fastest to restore.
- Differential backup: Only changes since the last full backup. Moderate speed both ways.
- Incremental backup: Only changes since the last backup of any type. Fastest to create, slowest to restore (multiple files needed).
Most DR strategies combine full weekly backups with daily incrementals to balance RPO and storage cost.
-- Log each backup with its type for audit and recovery planning
CREATE TABLE backup_log (
backup_id SERIAL PRIMARY KEY,
db_name VARCHAR(100) NOT NULL,
backup_type VARCHAR(20) CHECK (backup_type IN ('FULL', 'DIFFERENTIAL', 'INCREMENTAL')),
started_at TIMESTAMP NOT NULL,
finished_at TIMESTAMP,
size_mb NUMERIC(12, 2),
status VARCHAR(20) DEFAULT 'IN_PROGRESS'
);
INSERT INTO backup_log (db_name, backup_type, started_at, finished_at, size_mb, status) VALUES
('orders_db', 'FULL', '2024-06-01 01:00:00', '2024-06-01 02:15:00', 45200, 'SUCCESS'),
('orders_db', 'INCREMENTAL', '2024-06-02 01:00:00', '2024-06-02 01:08:00', 320, 'SUCCESS'),
('orders_db', 'INCREMENTAL', '2024-06-03 01:00:00', '2024-06-03 01:07:00', 290, 'SUCCESS');Point-in-Time Recovery (PITR)
Point-in-Time Recovery allows you to restore a database to any specific moment, not just the time of the last backup. This is achieved by replaying transaction logs (WAL in PostgreSQL) on top of a base backup.
PITR is essential when you need to recover from accidental data corruption or deletion that happened at a known time. You can restore to just before the damaging event occurred.
-- Record WAL archive events for PITR tracking
CREATE TABLE wal_archive_log (
segment_name VARCHAR(200) PRIMARY KEY,
archived_at TIMESTAMP DEFAULT NOW(),
size_bytes BIGINT,
storage_path TEXT
);
-- Find all WAL segments archived within a recovery window
SELECT
segment_name,
archived_at,
ROUND(size_bytes / 1024.0 / 1024.0, 2) AS size_mb
FROM wal_archive_log
WHERE archived_at BETWEEN '2024-06-03 09:00:00' AND '2024-06-03 11:00:00'
ORDER BY archived_at;Standby Databases and Replication
A standby (replica) database is a continuously updated copy of the primary database running on separate hardware. It serves two DR purposes:
- Hot standby: Can accept read queries and fails over in seconds (near-zero RTO).
- Warm standby: Kept in sync but not serving traffic; failover takes minutes.
Monitoring replication lag is critical — a lagging replica means your actual RPO is worse than expected.
-- Monitor replication lag on a PostgreSQL primary
SELECT
client_addr,
application_name,
state,
sent_lsn,
replay_lsn,
(sent_lsn - replay_lsn) AS lag_bytes,
EXTRACT(EPOCH FROM (NOW() - reply_time)) AS seconds_since_reply
FROM pg_stat_replication
ORDER BY lag_bytes DESC;Runbooks: Documenting Recovery Procedures
A runbook is a documented set of step-by-step instructions that an operator follows during a disaster recovery event. Without a runbook, even experienced DBAs make costly mistakes under pressure.
A good DR runbook includes: who to contact, what systems are affected, exact commands to run, expected outputs at each step, and rollback procedures if recovery fails. Storing runbook metadata in a database helps track versioning and audit usage.
-- Store runbook metadata in the database for audit tracking
CREATE TABLE runbook (
runbook_id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
scenario VARCHAR(100),
version VARCHAR(20) DEFAULT '1.0',
last_tested DATE,
owner VARCHAR(100),
doc_url TEXT
);
INSERT INTO runbook (title, scenario, version, last_tested, owner, doc_url) VALUES
('Full Database Restore from S3', 'total_loss', '2.1', '2024-05-15', 'dba_team', 'https://wiki.internal/dr/full-restore'),
('Failover to Hot Standby', 'primary_down', '1.4', '2024-04-20', 'dba_team', 'https://wiki.internal/dr/failover'),
('PITR to Specific Timestamp', 'data_corruption','1.2', '2024-03-10', 'dba_team', 'https://wiki.internal/dr/pitr');Runbook Execution Logging
Every time a runbook is executed — whether in a real disaster or a drill — it should be logged. Execution logs let you measure how long recovery actually takes (validating your RTO), identify steps that are slow or error-prone, and demonstrate compliance to auditors.
-- Log each runbook execution for RTO validation and audit
CREATE TABLE runbook_execution (
execution_id SERIAL PRIMARY KEY,
runbook_id INT REFERENCES runbook(runbook_id),
triggered_by VARCHAR(100),
is_drill BOOLEAN DEFAULT FALSE,
started_at TIMESTAMP NOT NULL,
completed_at TIMESTAMP,
outcome VARCHAR(20) CHECK (outcome IN ('SUCCESS', 'PARTIAL', 'FAILED'))
);
-- Report average restore time per runbook
SELECT
r.title,
COUNT(*) AS executions,
ROUND(AVG(EXTRACT(EPOCH FROM (e.completed_at - e.started_at)) / 60), 1) AS avg_minutes,
MAX(EXTRACT(EPOCH FROM (e.completed_at - e.started_at)) / 60) AS max_minutes
FROM runbook_execution e
JOIN runbook r ON r.runbook_id = e.runbook_id
WHERE e.outcome = 'SUCCESS'
GROUP BY r.title;DR Testing: Regular Drills
A DR plan that has never been tested is not a plan — it is a wish. Regular drills are mandatory to ensure your team can actually execute the runbooks within the defined RTO and that backups are genuinely restorable.
Scheduling and tracking drills in your database creates an audit trail and helps you surface runbooks that are overdue for testing.
-- Find runbooks that have not been drilled in over 90 days
SELECT
r.runbook_id,
r.title,
r.scenario,
MAX(e.completed_at) AS last_drill,
CURRENT_DATE - MAX(e.completed_at::DATE) AS days_since_drill
FROM runbook r
LEFT JOIN runbook_execution e
ON e.runbook_id = r.runbook_id
AND e.is_drill = TRUE
AND e.outcome = 'SUCCESS'
GROUP BY r.runbook_id, r.title, r.scenario
HAVING MAX(e.completed_at) IS NULL
OR CURRENT_DATE - MAX(e.completed_at::DATE) > 90
ORDER BY days_since_drill DESC NULLS FIRST;Backup Retention Policies
Retention policies define how long backups are kept. Keeping every backup forever wastes storage; deleting them too quickly violates your RPO and compliance requirements.
A common policy is: daily incrementals for 7 days, weekly fulls for 4 weeks, and monthly fulls for 12 months. You can enforce and audit retention with SQL queries against your backup log.
-- Identify backups that are outside their retention window and ready to purge
SELECT
backup_id,
db_name,
backup_type,
finished_at,
CURRENT_DATE - finished_at::DATE AS age_days,
CASE backup_type
WHEN 'INCREMENTAL' THEN 7
WHEN 'DIFFERENTIAL' THEN 28
WHEN 'FULL' THEN 365
END AS retention_days,
CASE
WHEN (CURRENT_DATE - finished_at::DATE) >
CASE backup_type
WHEN 'INCREMENTAL' THEN 7
WHEN 'DIFFERENTIAL' THEN 28
WHEN 'FULL' THEN 365
END
THEN 'PURGE'
ELSE 'KEEP'
END AS action
FROM backup_log
WHERE status = 'SUCCESS'
ORDER BY finished_at;Quick Check
Test your understanding of RPO and RTO definitions.
Recap: Disaster Recovery Planning
In this lesson you explored the foundations of database disaster recovery planning:
- RPO defines the maximum tolerable data loss in time; a shorter RPO requires more frequent backups or continuous replication.
- RTO defines how quickly the system must be restored; a shorter RTO demands hot standbys and automated failover.
- Backup types — full, differential, incremental — each offer different trade-offs between storage, creation speed, and restore speed.
- PITR (Point-in-Time Recovery) uses transaction logs to restore to any precise moment, protecting against accidental changes.
- Runbooks document every step of a recovery procedure; logging executions validates that your RTO is achievable in practice.
- Regular DR drills are the only way to confirm that backups are restorable and that your team can meet RPO and RTO targets under real pressure.
- Retention policies balance storage cost against compliance and recovery requirements.
A well-tested DR plan is one of the most valuable investments a database team can make.
Frequently asked questions
Is the “Disaster Recovery Planning” lesson free?
Yes — the full text of “Disaster Recovery Planning” is free to read here on the web, and the SQL Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the SQL Academy course, upgrade to CoddyKit PRO.
What will I learn in “Disaster Recovery Planning”?
RPO, RTO and runbooks. You practise SQL Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start SQL Academy?
No prior experience is required. SQL Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Disaster Recovery Planning” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this SQL Academy lesson?
Yes. Every SQL Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Logical vs Physical Backups
- Point-in-Time Recovery
- Testing Your Restores
- Disaster Recovery Planning