Build a recovery plan before an Energon 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
When this checkout has backup-this-energon , that skill is the capture SOP. This page remains the recovery contract and restore runbook; the skill does not walk restore. An ordinary upgrade records a Time Travel bookmark and does not require this skill or a durable R2 copy. A bookmark alone is not a backup of published files. If there is no independent R2 or S3 remote, the generation is D1-only and incomplete.
Preserve the configured source
Recovery needs the private deployment repository and read access for the recovering operator, as well as D1 and R2 backups. Record the repository coordinates and source commit with the backup generation. Keep generated plugin identity and instance configuration with that source. The public upstream cannot recover company customizations, and a Git clone does not back up published data. Re-provision GitHub Actions and Cloudflare credentials through secret stores; do not put credential values into backup manifests.
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 \
--downloadA copy may include a few short-lived objects under tmp/, which Energon uses to stage uploads and rollback snapshots over 25 MB. They are not catalog content; skip them on restore. From v1.10.0, a copy can also include staged deployment files and superseded site versions that are waiting for cleanup. Copy them anyway: D1 tracks them as storage allocations, and normal cleanup reclaims them after restore.
The 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 Energon 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. Loose files live under
files/{id}/{filename}. From v1.10.0, converted site bytes live undersite-versions/{site id}/{version id}/…and are found throughsite_version_files.object_key, not by path; older releases and unconverted sites usesites/{handle}/{slug}/{path}. - 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 outside
tmp/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, guest write withX-Energon-Write-Passwordwhen a fixture has one, 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 Energon 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:
- From v1.10.0, every path of each live site’s active version (
site_version_filesforsites.active_version_id) maps to its storedobject_key. Unconverted and older sites map eachsite_filesrow 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, plus, from v1.10.0, the charges for open deployments, retained versions, and pending cleanup. Use Recompute quota on Admin rather than editing the ledger.- 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.