Build a recovery plan before an Energon instance holds work that cannot simply be published again. This preparation matters because Energon keeps catalog state in D1 and file bytes in R2, so either service recovering on its own can leave incomplete links. The operator must choose the acceptable recovery window, keep an independent copy of R2, and rehearse restoring both sides as one deployment unit.
Back up and restore D1 and R2
Start with an explicit recovery contract
Choose these values with the people who own the content. Energon does not set them for you:
| Decision | Record |
|---|---|
| Recovery point objective (RPO) | Maximum acceptable age of recovered catalog and bytes. |
| Recovery time objective (RTO) | Maximum acceptable time to stop writes, restore, verify, and reopen. |
| Retention | How many daily, weekly, or monthly backup generations remain available. |
| Backup boundary | A separate bucket, account, or storage provider with separately scoped credentials. |
| Restore owner | The human authorized to alter production D1, R2 bindings, routes, and deployments. |
| Drill cadence | How often a disposable restore proves the procedure and measured RTO. |
Cloudflare D1 Time Travel is always enabled for production-storage databases and supports point-in-time recovery within the plan’s retention window. It is a fast defense against recent catalog mistakes, not the long-term backup policy. Cloudflare also documents a scheduled D1 export workflow for retaining SQL dumps beyond that window.
R2 is the live object store. Energon does not retain old object revisions after replace or delete. Use R2’s S3-compatible API or Cloudflare’s rclone integration to copy objects to an independent backup boundary.
Back up both halves
1. Preserve D1 beyond Time Travel when the RPO requires it
Time Travel is appropriate for a recent accidental migration, update, or deletion. Before depending on it, confirm the database uses the production storage subsystem and record the plan’s actual retention. For longer retention, schedule an export to a backup location with alerting on missed or failed runs.
npx wrangler d1 info energon
npx wrangler d1 time-travel info energonDo not store the only long-term D1 export beside the only copy of the R2 objects under one credential and failure boundary. Copy retained exports onward if the scheduled workflow initially lands them in R2.
2. Copy R2 without propagating deletions
Create read-only credentials for the live bucket and write-only or narrowly scoped credentials for the backup target where your tooling permits it. Use a timestamped destination and copy, not a mirroring command that deletes backup objects missing from the source.
rclone copy energon-primary:energon energon-backup:energon/2026-09-03T20-00Z \
--metadata --checksum
rclone check energon-primary:energon energon-backup:energon/2026-09-03T20-00Z \
--downloadThe remote names and timestamp above are examples, not production credentials. Keep credentials outside the repository, capture command exit status and object counts, and alert when either the copy or verification fails.
3. Record one recovery manifest
For each backup generation, record the D1 restore timestamp or export identifier, R2 snapshot path, start and completion times, object count, byte count, verification result, source commit, and operator. A continuously changing instance cannot promise a transactionally consistent D1-plus-R2 snapshot. If the business requires zero-skew backups, use an audited edge control to block mutating /v1 methods during the capture window and verify that the block works before taking either snapshot.
Choose the smallest recovery
| Incident | Preferred recovery |
|---|---|
| Recent D1-only mistake | Use Time Travel after recording the current bookmark, then verify every referenced object. |
| Deleted or corrupted R2 objects | Copy the affected keys from an independent generation; do not roll back unrelated catalog changes. |
| Lost bucket or uncertain object set | Restore into a new bucket, verify it, then change the Worker binding in a reviewed deployment. |
| Catalog and bytes both uncertain | Restore a matched D1 and R2 generation into disposable resources, reconcile, then cut over. |
| Leaked API or backup credential | Revoke or rotate it first, preserve evidence, then assess whether content or catalog restoration is needed. |
Restore through disposable resources
- Pause agents and temporarily block mutating
/v1requests using your documented Cloudflare edge control. Leave health checks readable, and verify a write is rejected before continuing. - Preserve the incident state: record the current D1 bookmark, copy relevant R2 objects if safe, and save logs and timestamps.
- Create a new R2 bucket and restore the chosen generation with the original keys. Energon expects site bytes under
sites/{handle}/{slug}/{path}and loose files underfiles/{id}/{filename}. - Restore D1 to a disposable database from the chosen export. For a recent in-place Time Travel recovery, remember that Cloudflare describes the operation as destructive and returns a bookmark that can undo it.
- Point a non-public Worker deployment at the restored D1 and R2 resources. Apply only migrations that come after the restored schema version, in normal order.
- Compare the catalog with the object listing. Treat a D1 row with no corresponding R2 object as missing content. Treat an unreferenced R2 key as an orphan to investigate, not something to delete during the incident.
- Exercise
/v1/health,/v1/help, authenticated reads, public Markdown, a site asset, password-gated content, and expiry behavior. Verify byte size and content type as well as HTTP status. - Have the recovery owner approve the production binding or route change. Reopen writes gradually, watch errors and storage totals, and retain the pre-cutover resources until the rollback window closes.
- If the restore is abandoned, name the incident owner and choose an explicit exit: keep writes blocked while the original instance remains unsafe, or return routes and bindings to a known-good pre-incident state before lifting the block and verifying a normal write. Retain the disposable resources and record where the attempt stopped before replanning.
Do not restore R2 over the live bucket first, delete unexplained objects, stamp d1_migrations, or run improvised production SQL. Preserve the incident state and recover into new resources whenever the failure scope is uncertain.
Reconcile the catalog and object store
A successful /v1/health response proves only that the Worker route is live; it does not verify D1 or R2. The authenticated reads and public object checks in step 7 establish binding reachability, but the restore is complete only when these relationships hold:
- Every
site_filesrow maps tosites/{handle}/{slug}/{path}. - Every
loose_filesrow maps tofiles/{id}/{filename}. platform_quota.usedagrees with the sum of live catalog sizes after the restored schema’s expiry rules are applied.- Each retained object is readable with the stored content type and expected byte size.
- Backup-only temporary prefixes are not bound as the live bucket.
If a referenced object is absent from every retained generation, leave the item unavailable and ask its owner to republish it. Do not fabricate bytes or silently delete its catalog row just to make counts agree.
Drill before the incident
Run the full procedure against disposable D1 and R2 resources on the agreed cadence. Record the achieved RPO and RTO, sample large and small objects, verify a Markdown file and a multi-file site in a browser, and prove the rollback path before destroying the drill resources. A backup that has never completed a restore drill is an untested input, not a recovery capability.
For migration-specific failures and rollback floors, use Upgrade and recover. Review Architecture and integrity for the D1/R2 mutation contract and Security model before granting backup credentials.