Cloudflare D1 backup recovery workflow with R2 exports and restore drills
A practical, layered backup and recovery workflow for Cloudflare D1 using Time Travel and R2, with concrete RPO/RTO targets and restorable-backup drills.
1. What “recoverable” means for a D1 backup workflow
If your app runs on Cloudflare Workers with D1 as the primary transactional store, a backup is only useful if you can restore it under pressure. For a small team, a recoverable D1 backup workflow means:
- Clear objectives: defined recovery point objective (RPO) and recovery time objective (RTO) that match your product and headcount.
- Layered recovery paths: fast, short-window recovery via D1 Time Travel, plus scheduled SQL exports stored in R2 for independent retention.
- Proven drills: periodic restore tests into fresh D1 databases, with explicit checks so you can prove the backups are usable.
Cloudflare’s D1 documentation describes D1 as a serverless SQL database built on SQLite, integrated with Workers and the Cloudflare network. It is billed on a usage basis (storage and queries) rather than per‑database fees, and current limits differ between the Free and Workers Paid plans (for example 10 databases and 500 MB per database on Free; up to 50,000 databases per account and 10 GB per database on Workers Paid as of April 21, 2026) (Cloudflare D1 overview). That model encourages many small databases, which influences your backup design and how you structure the rest of your production workflow alongside platforms like Supabase (Supabase review for AI builders) and your choice of deployment stack (safe Vercel deployment workflow).
1.1 Practical RPO/RTO targets for Cloudflare-native apps
For early-stage SaaS and internal tools built on D1, realistic starting targets are:
- RPO (how much data you can lose): 15–60 minutes for most user-facing apps, looser (up to 24 hours) for non-critical telemetry or logs.
- RTO (how long you can be down): 15–60 minutes for simple single-database apps; 1–2 hours for larger or sharded designs.
These numbers are not tied to Cloudflare guarantees; they are workable ranges for small teams without 24/7 SRE coverage and should sit alongside your deployment and incident processes (for example how you roll code with GitHub Actions or Vercel (safe Vercel deployment workflow) or run schema changes safely on other backends (safe database migration workflow)).
1.2 Failure modes your D1 workflow should cover
A recoverable workflow for D1 must explicitly address at least:
- Operator error: accidental
DELETE, bad bulk update, dropping a table.
- Bad migrations: schema changes that corrupt or lose data.
- Application bugs: code that gradually writes incorrect data.
- Logical corruption: bad data introduced by integrations or misconfigured jobs.
- Account or control-plane issues: Cloudflare account compromise or loss, misconfigured permissions, or region-wide incidents.
1.3 D1’s main recovery paths
From Cloudflare’s documentation, there are effectively two backup/recovery mechanisms you may encounter, depending on your storage subsystem: (1) Time Travel, which is the default for databases on the new storage subsystem, and (2) the legacy snapshot‑based backup API, which only applies to older databases still on the alpha storage subsystem. In both cases you can also export/import SQL dumps via Wrangler for additional durability and migration use cases.
- Time Travel restore on the current storage system – D1 continuously creates point-in-time restore checkpoints without manual scheduling (Time Travel docs). Restores can be executed via Wrangler, for example:
wrangler d1 time-travel restore <database> --timestamp="2023-09-23T14:20:00Z" (Cloudflare D1 open beta announcement).
- Import from SQL dump – D1 supports exporting a database as a
.sql file with wrangler d1 export and restoring from a dump using wrangler d1 execute --file (import/export docs).
- Legacy snapshot backup API – some older databases on the alpha storage subsystem still use a snapshot-based backup API (legacy backups docs).
Time Travel is the fastest way to undo recent mistakes, but it is bound to your Cloudflare account and the D1 control plane. SQL exports, especially when stored in R2, give you independent retention and a route for disaster recovery scenarios where the account or region is part of the problem.
2. Understanding D1’s native durability: Time Travel and legacy backups
Cloudflare D1 runs on SQLite under the hood and is positioned alongside R2, KV, Durable Objects and Queues as part of Cloudflare’s developer data platform (Cloudflare developer platform overview). The storage layer and backup features have evolved over time, so you must first confirm which storage version your database is using.
Cloudflare’s official D1 Time Travel and backups reference shows that Time Travel is always enabled on databases using the new storage subsystem and replaces the legacy snapshot-based backup API, which is the foundation for the workflow described in this section.
2.1 How Time Travel works
According to the Time Travel documentation, D1:
- Maintains continuous point-in-time restore checkpoints for each database without requiring you to schedule backup jobs (Time Travel docs).
- Allows you to restore a database to a specific timestamp using Wrangler or APIs, with syntax similar to:
wrangler d1 time-travel restore northwind --timestamp="2023-09-23T14:20:00Z" (Cloudflare D1 open beta announcement).
- Supports exporting your D1 database to external storage (for example R2) via REST APIs or Cloudflare Workflows, allowing you to store SQL dumps outside the primary D1 control plane (Time Travel docs).
Cloudflare’s Time Travel documentation notes that you can automatically export your D1 database into R2 storage via REST API and Cloudflare Workflows; many teams treat those external exports as a complementary long-term durability layer rather than relying only on Time Travel’s 7–30 day window (Time Travel docs).
2.2 Constraints and behaviour during Time Travel restores
The Time Travel docs focus on the mechanics of selecting a timestamp and restoring, but there are operational implications you should assume when designing a workflow:
- Scope: Time Travel operates per database; you must coordinate timestamps across multiple databases if your app uses more than one.
- Traffic coordination: during a restore, your application should either be taken offline or placed into a controlled mode (for example maintenance page or read-only behaviour) to avoid writes hitting a changing state.
- Window: Cloudflare does not promise specific retention windows in the high-level docs, so treat Time Travel as a short-to-medium window safety net rather than long-term archive.
2.3 Identifying legacy alpha databases
D1’s backup documentation explains that only databases on the old alpha storage subsystem use the legacy snapshot backup API (legacy backups docs). You can check this via Wrangler:
wrangler d1 info <database>
The version field in the output tells you which system you are on. The legacy backups documentation explains that databases on the old alpha storage subsystem use the snapshot-based backup API, and that Time Travel is available on the newer storage subsystem and replaces those legacy backups there. Databases reporting version: alpha in wrangler d1 info are on the legacy path (legacy backups docs).
2.4 When the legacy snapshot API still matters
If (and only if) your production database is still on the alpha storage subsystem (that is, wrangler d1 info reports version: alpha):
- Your primary point-in-time recovery path is the legacy snapshot backup API, not Time Travel.
- You should still export SQL dumps for long-term durability and for migration planning.
- A migration to the newer storage system should be treated as an explicit project once your app is stable, in order to benefit from Time Travel and newer durability features.
This is the first “decision flip”: if version: alpha appears, you must centre your workflow on the legacy backups plus exports rather than Time Travel.
2.5 Time Travel vs exports: what each is for
Combining the docs and typical DR patterns, a simple rule of thumb emerges:
- Time Travel: quick rollback of recent mistakes and logical corruption within the Time Travel window. Primary path for operator errors, bad migrations and short-term bugs.
- Exports to R2: long-term retention, cross-account or cross-cloud copy, and recovery from scenarios where Time Travel is not available or your Cloudflare account is the problem.
3. Exporting D1 safely: SQL dumps, R2 copies, and scheduling
The D1 import/export documentation shows two key mechanisms (import/export docs):
- Local export:
wrangler d1 export <database> --output=backup.sql
- Local import:
wrangler d1 execute <database> --file=backup.sql
The same page documents a known limitation: imports via wrangler d1 execute --file are limited to 5 GiB per SQL file, matching R2’s upload limit. This is a constraint on individual import files (not on total database size) that becomes important as your dataset grows.
3.1 How exports are billed
Cloudflare’s D1 pricing documentation states that D1 is billed based on storage and query usage, with no separate per‑database fee, and that there are no egress or bandwidth charges for data accessed directly from D1 (D1 pricing). As of April 21, 2026, limits such as maximum databases per account and per-database size are documented separately under the D1 limits page (limits docs). The docs emphasise that usage is measured in rows read and written, not in arbitrary “backup units”.
From that, a normalised cost model for exports looks like this:
- A full export reads most or all rows in the database once.
- Daily full exports → roughly 30× database size worth of reads per month.
- Six-hourly exports (4 per day) → roughly 120× database size worth of reads per month.
- Weekly exports → roughly 4× database size worth of reads per month.
Community discussions around D1 migrations report unexpected billing spikes when exporting or migrating large datasets because each row read is billable (community billing discussion). Backup exports should be treated the same way: they add predictable read usage you need to budget for alongside other platform costs such as AI agents or automation workflows (AI adoption cost for SMEs).
3.2 Automating D1 → R2 backups with Workflows
Cloudflare Workflows includes an example that regularly exports D1 data via REST API and stores SQL dumps into an R2 bucket on a schedule (Workflows D1 backup example). The pattern is:
- A cron trigger starts the workflow on a defined cadence (for example every hour or every 6 hours).
- The workflow calls a D1 backup/ export endpoint exposed over REST, obtaining a SQL representation of the database.
- The workflow writes that SQL dump to an R2 bucket as an object, typically with a
.sql extension and timestamped name.
3.3 Bucket layout and naming conventions
For fast recovery under incident pressure, object keys need to be easy to scan and sort. A pragmatic structure:
d1/<env>/<db-name>/<YYYY>/<MM>/<DD>/backup-<timestamp>.sql
# Example
d1/prod/billing/2026/03/01/backup-2026-03-01T12:00:00Z.sql
Benefits:
- Lexicographic sort by key aligns with time order.
- It is easy to scope retention rules by environment and database.
- Operators can eyeball the path during an incident.
3.4 Choosing export cadence from your RPO
Using the cost assumptions above, mapping cadence to effective RPO:
| Backup cadence |
Typical RPO contribution from exports |
Read volume multiplier / month |
| Hourly full export |
≤ 60 minutes (if Time Travel unavailable) |
≈ 720× DB size |
| Every 6 hours |
≤ 6 hours |
≈ 120× DB size |
| Daily |
≤ 24 hours |
≈ 30× DB size |
| Weekly |
≤ 7 days |
≈ 4× DB size |
In practice:
- For a solo builder with <1 GB databases, daily exports are often sufficient.
- For a team with stricter RPO (for example payments or high-frequency writes), 6-hourly exports plus Time Travel is a common compromise.
- For cost-constrained, low-change workloads, weekly exports plus Time Travel may be acceptable.
3.5 Dealing with the 5 GiB import limit
The import/export docs state that wrangler d1 execute --file imports are limited to 5 GiB per file (import/export docs). For larger databases, especially those on the Workers Paid plan approaching the 10 GB maximum database size (or the 500 MB cap on the Free plan), a single full SQL dump risks exceeding the 5 GiB per-file import limit, depending on schema and data characteristics (limits docs).
In that case, your export strategy must change:
- Export per-table or per-logical-group dumps (for example, high-value tables in separate files).
- Document a restore order (for example reference tables → core entities → events/logs).
- Accept a longer RTO during disaster recovery because multiple imports are required.
This is another “decision flip”: once you are near the documented storage limits or the 5 GiB per-file import limit, you move from one-click restores to sharded or multi-file runbooks.
4. Designing an end-to-end recovery workflow using Time Travel
For databases on D1’s new storage subsystem with Time Travel available, Time Travel is your first line of defence for recent, human-caused problems. The objective is to make it safe, predictable and testable.
4.1 When to use Time Travel
Time Travel is the primary recovery path when:
- A bad migration ran in the last few hours.
- An operator executed a destructive query (for example
DELETE FROM users without a WHERE clause).
- A deployment introduced a bug that wrote incorrect data for a short, known period.
In these cases, you want to roll back to a point just before the mistake, losing as little data as possible.
4.2 Step-by-step Time Travel restore (safe pattern)
Because the exact administrative UI might change, a safe pattern derived from the documentation is:
- Identify the incident window
- From logs, alerts or deploy history, narrow down when bad writes started.
- Pick a candidate timestamp just before that window (for example, one minute before the bad migration started).
- Create a staging database
- Provision a new D1 database in the same account (for example
myapp-prod-restore-test).
- This database is only for validating the restore; production remains untouched for now.
- Restore into staging
Use the CLI pattern demonstrated in the Cloudflare blog (D1 open beta announcement), adapting it if the Time Travel docs show a slightly different flag set:
wrangler d1 time-travel restore \
myapp-prod \
--timestamp="2026-03-01T12:00:00Z"
If the current tooling does not directly support restoring into a separate database, an alternative is a two-step process using a Time Travel export followed by an import into the staging database, following the patterns in the Time Travel and import/export docs.
- Validate staging
- Run the verification checks described in section 6: row counts, integrity queries, key workflows.
- Confirm that the data reflects the expected state just before the incident.
- Execute production restore
- Place the app into maintenance mode.
- Repeat the restore for the real production database with the validated timestamp.
- Only bring the app back online once post-restore checks pass.
4.3 Managing traffic during Time Travel restores
To avoid inconsistent user experience:
- Expose a maintenance page via Workers Routes while the restore runs.
- For apps with critical read flows, consider a read-only mode where writes are blocked at the application level for the duration of the restore.
- Communicate expected downtime windows internally based on drill data (see section 6.4).
4.4 Post-restore validation checks
After a Time Travel restore, consider the restore complete only when:
- Key tables have expected row counts (for example compare to metrics recorded before the incident).
- Referential integrity is intact (run queries that ensure foreign key relationships are consistent where applicable).
- Deterministic queries against D1 return known values (for example, the number of active paid subscriptions, or a specific test tenant’s data).
- Application smoke tests pass for core user flows (login, primary dashboard, main write operation).
5. Designing an export-based recovery workflow with R2
Time Travel covers short-window incidents; exports to R2 cover longer-term and account-level disasters. A recoverable workflow always includes a robust export path.
5.1 End-to-end export-to-R2 flow
Based on Cloudflare’s Workflows example and import/export docs, a common architecture is:
- Schedule: A Workflows cron trigger runs every N minutes/hours.
- Export: The workflow or a Worker calls the D1 backup API or runs a D1 export via REST, producing a SQL dump.
- Store: The SQL is written as an object in an R2 bucket, using a timestamped key as described in section 3.3.
- Replicate: An optional secondary workflow copies new objects to a second R2 bucket in another Cloudflare account or a different provider, meeting stricter DR policies.
- Prune: R2 lifecycle rules automatically delete older objects according to retention policies.
5.2 Locking down R2 access
For security and blast radius control:
- Create an R2 bucket dedicated to backups and restrict access via scoped API tokens or service bindings.
- Separate write access (for Workflows/backup jobs) from read-only access (for restore jobs and auditors).
- For stricter organisations, maintain a secondary Cloudflare account with an R2 bucket that only accepts replicated backups, with no write access from your main production account.
These measures address the decision flip where DR policies require recoverability even after a full Cloudflare account loss or compromise.
5.3 Restoring from an R2 backup
Restoring from R2 involves:
- Fetch the dump
- Download the desired
.sql file from R2 to a secure operator machine, or
- Use a Worker or CLI tool that streams the SQL from R2 into
wrangler d1 execute.
- Create a new D1 database for restore (for example
myapp-prod-2026-03-01-restore).
- Run the import using the import/export pattern from the docs (import/export docs):
wrangler d1 execute myapp-prod-2026-03-01-restore \
--file=backup-2026-03-01T12:00:00Z.sql
- Validate using the checks from section 6.
- Cut over by pointing your application to the restored database (for example via environment variables) and retiring the old database once stable.
5.4 Handling large datasets and chunked restores
For larger datasets:
- Export core tables separately so they remain below 5 GiB each.
- In runbooks, document restore order and approximate durations based on drills.
- Consider sharding by tenant or time (for example one D1 database per large enterprise tenant, or by year) so each database stays within the documented D1 size limits (limits docs).
In these scenarios, RTO inevitably increases. That trade-off should be made explicit in your SLOs.
5.5 When export-based recovery is primary
Exports should be treated as the authoritative recovery path when:
- You need to restore data that is older than the Time Travel window.
- You assume a Cloudflare account compromise and plan to rebuild into a new account using R2 cross-account copies.
- You need to move or fork production data into another environment (for example for privacy-compliant analytics or migration to a new platform).
6. Verifying that a backup is actually restorable
A D1 backup is “recoverable” only when you can demonstrate that it can be restored into a functioning database that your app can run against. This is where restore drills and verification checks come in.
6.1 Verification goals
Each drill should prove three properties:
- Structural integrity: the schema (tables, indexes, constraints) in the restored database matches production.
- Critical data presence: key entities (for example users, subscriptions, configs) are present and internally consistent.
- Application-level behaviour: core workflows succeed when the app is pointed at the restored database.
6.2 Running a periodic restore drill
A practical monthly or weekly drill for a small team looks like:
- Choose the latest R2 backup for one production D1 database.
- Provision a temporary D1 database (for example
myapp-prod-drill-2026-03-01).
- Import the SQL dump with
wrangler d1 execute --file.
- Run verification queries:
- Row counts for critical tables (for example
SELECT COUNT(*) FROM users;).
- Foreign key or referential sanity checks.
- Checks for sentinel data (for example a specific test user or organisation).
- Point a staging or test instance of your app at the restored database and run automated smoke tests.
- Record metrics: when the drill started, when import completed, when tests passed, and any errors.
- Destroy the temporary database to avoid clutter and surprise costs.
This drill can start manually and then be semi-automated via CI or scheduled jobs. It pairs well with broader disaster recovery workflows for your stack (AI development workflow from prompt to production).
6.3 Concrete verification signals
To make “backup is restorable” objective, define signals such as:
- Import success:
wrangler d1 execute --file exits without errors; logs show expected number of statements run.
- Row count thresholds:
- For each critical table,
COUNT(*) is within an expected range (for example within ±1% of the latest production metric).
- Referential queries:
- Queries like
SELECT COUNT(*) FROM child LEFT JOIN parent ON... WHERE parent.id IS NULL; return 0 or a small, known value.
- Deterministic sanity checks:
- Known test tenant returns expected data; a reference invoice or subscription can be read.
- Application test pass rate:
- A targeted suite of end-to-end tests (for example login, create order, view dashboard) passes against the restored database.
6.4 Measuring restore duration for RTO
During each drill, capture:
- T0: timestamp when you start the import.
- T1: when
wrangler d1 execute --file completes.
- T2: when verification queries finish.
- T3: when application-level tests complete successfully.
Your observed technical restore time is T1 - T0. Your operational restore time (RTO) is closer to T3 - T0, because in production you need both import and basic validation before reopening traffic.
6.5 Turning drills into CI checks
Once the manual pattern is stable, you can automate parts of it in CI/CD:
- A nightly or weekly CI job (for example GitHub Actions) downloads the latest R2 dump, spins up a temporary D1 database, imports the dump, runs SQL checks and a small test suite, then tears it down.
- The job records metrics and surfaces failures as alerts; this aligns with common practices where automated checks gate releases and catch regressions before production.
6.6 Recording drill results
Keep a simple log, for example in a runbook or internal wiki, that records:
- Date of last successful drill.
- Database(s) tested and backup timestamp.
- Observed restore time.
- Any anomalies or data issues found.
- Follow-up actions (schema fixes, index changes, backup cadence changes).
This turns “backups should work” into “backups were verified to work on specific dates and within known time ranges”.
7. Retention strategy and cost trade-offs for small teams
Cloudflare’s pricing model for D1 and R2 is usage-based. Exact per-unit prices are not contained in the docs referenced here, so concrete currency figures must be taken from the current pricing pages or billing UI (D1 pricing), (Cloudflare plans). The relative costs and trade-offs can still be reasoned about.
7.1 Realistic retention goals
For most small teams:
- Time Travel: treat as 7–30 days of short-window safety (actual value depends on Cloudflare’s current policies; check Time Travel docs and UI).
- R2 exports:
- Keep 7–30 days of hourly or 6-hourly backups.
- Keep 30–90 days of daily backups.
- Optionally keep 12–24 monthly snapshots for audit and deep history.
7.2 R2 object count implications
Approximate number of backup files per database:
| Cadence |
Files per day |
Files in 30 days |
Files in 365 days |
| Hourly |
24 |
≈ 720 |
≈ 8,760 |
| Every 6 hours |
4 |
≈ 120 |
≈ 1,460 |
| Daily |
1 |
≈ 30 |
≈ 365 |
| Weekly |
0.14 |
≈ 4 |
≈ 52 |
Since R2 pricing is usage-based, storing thousands of small SQL dumps is mostly a linear storage cost problem; exact figures depend on current R2 rates.
7.3 Lifecycle rules
To keep R2 storage growth under control:
- Use bucket lifecycle rules to delete hourly/6-hourly backups after 7–30 days.
- Keep only daily backups beyond 30 days, up to an agreed retention (for example 90–365 days).
- Optionally mark one backup per month as a long-term snapshot and exclude it from automatic deletion.
7.4 Cost scenarios (normalized analysis)
Using the scenarios from the brief and the export read multipliers above:
- Solo builder, <1 GB DB, daily exports:
- Reads: ≈ 30× DB size per month.
- R2 storage: roughly 30 new dumps per month per database before lifecycle pruning.
- Conclusion: export cost is modest and predictable; Time Travel covers short-window issues.
- Small team, 3–5 GB DBs, 6-hourly exports:
- Reads: ≈ 120× DB size per month (roughly 4× daily exports).
- R2 storage: 120 backups per month per database at this cadence.
- Conclusion: materially higher query usage than daily; justified only if RPO demands are tight.
- Cost-constrained, low-change workloads, weekly exports:
- Reads: ≈ 4× DB size per month.
- R2 storage: 4 backups per month per database.
- Conclusion: minimal export overhead; rely heavily on Time Travel for recent incidents.
In all cases, drill frequency adds mainly operator time and a small amount of D1 query usage for verification queries.
8. Mapping incident types to recovery paths
To turn this into a runbook, map common incidents to concrete actions:
| Incident type |
Primary recovery path |
Expected RPO / RTO (typical small-team) |
Notes |
| Accidental destructive query in last 1–2 hours |
Time Travel restore to pre-incident timestamp |
RPO: < 15 minutes RTO: 15–45 minutes |
Use staging-then-prod pattern; validate with row counts. |
| Bad migration deployed earlier today |
Time Travel restore if within window; else R2 export from before migration |
RPO: ≤ 6–24 hours (depending on export cadence) RTO: 30–90 minutes |
Rollback app deployment as well; confirm schema alignment. |
| Quiet logical corruption over several days |
R2 exports from before corruption; possible partial replays |
RPO: depends on detection delay; could be days RTO: > 60 minutes |
Needs careful forensic work; consider exporting only key tables for comparison. |
| Cloudflare account compromise |
Cross-account or cross-cloud R2 backups |
RPO: tied to export cadence RTO: hours, depending on rebuild |
Requires pre-provisioned secondary environment and tested runbook. |
| Region or platform incident limiting Time Travel |
R2 exports and possibly legacy backups (if alpha) |
RPO: 6–24 hours typical RTO: 60–120 minutes |
Assumes R2 is accessible and D1 can be recreated in another region/account. |
This mapping should live in your internal incident playbook with concrete commands and URLs, ideally next to your other production-hygiene runbooks like schema migration workflows (safe database migration workflow) and deployment runbooks for your AI-heavy services (AI development workflow from prompt to production).
9. What changes the decision: key flips
The core thesis is that a layered system—Time Travel plus R2 exports plus restore drills—is the default for small teams. That flips in a few conditions:
- Legacy alpha databases → Centre your workflow on the legacy snapshot backup API plus regular exports, and plan a migration to the new storage system once viable.
- Near documented size and import limits → Move to sharded or multi-database designs; design per-shard exports and sequential restores.
- Strict DR policies (account-loss scenarios) → Add cross-account or cross-cloud replication of R2 SQL dumps as a first-class requirement.
- Very tight RPO (< 1 hour) with high write volume → Use Time Travel heavily and run more frequent exports of critical tables, accepting higher query usage.
- Export-related D1 costs spike → Trim export scope to critical tables and reduce cadence, leaning more on Time Travel while keeping at least weekly full exports.
10. A concrete, testable definition of “recoverable” for D1
Putting everything together, a small team can define a “recoverable” D1 workflow as:
Cloudflare’s R2 product documentation illustrates how objects are organized within buckets, grounding the proposed key structure for timestamped D1 backup SQL files and helping readers relate the env/db/YYYY/MM/DD naming convention to how R2 actually stores objects.
The official Workflows example for exporting and saving a D1 database into R2 demonstrates that Cloudflare supports scheduled D1 backups via cron-style triggers and a workflow that calls the D1 export API and writes SQL dumps into R2, matching the automation pattern recommended in this section.
The Wrangler commands reference documents the wrangler d1 info command and its output, including fields like the database version, which operators use to determine whether their database is on the legacy alpha storage subsystem and therefore must rely on the older snapshot-based backup API.
- Objectives:
- RPO ≤ 1 hour for core transactional data, ≤ 24 hours for non-critical data.
- RTO ≤ 60 minutes for single-database restores during working hours.
- Layers:
- Time Travel enabled and documented for each production D1 database where supported by the storage subsystem.
- Automated SQL exports to R2 at least daily (or more frequent for critical apps), with structured naming and lifecycle rules.
- For alpha-version databases, legacy backups configured and tested until migration.
- Verification:
- Monthly restore drills into fresh D1 databases from R2, with row-count and application-level checks.
- Measured restore durations logged and used to update realistic RTO commitments.
- Resilience:
- For higher-assurance teams, cross-account or cross-cloud replication of R2 dumps for account-loss scenarios.
- Runbooks mapping incident types to Time Travel vs export-based recovery, with step-by-step commands.
If a production setup meets these criteria and runbooks are kept current as D1 evolves, the result is a recoverable Cloudflare D1 backup workflow rather than just “having backups somewhere”.