Skip to main content
Back up three parts of a Cloudflare deployment: D1 metadata, each workspace’s Git history, and both R2 buckets. The tested restore rebuilds D1 and reconnects it to the existing Artifacts and R2 data.
The tested restore did not rebuild lost Artifacts or R2 resources. Keep the Git and R2 backups, but do not treat this procedure as a tested full-account restore. A Workspaces API key can clone only the workspaces its owner may read, so private workspaces may need separate owner keys.

Back up the deployment

Use a protected backup directory outside the source checkout. Do not place API keys, resource IDs, or customer data in the repository or a support request.

Export D1

From apps/rooms, export data without the schema:
Keep the source tag, D1 row counts, and export file together. The schema comes from the source migrations during restore.

Clone every workspace’s Git history

Read the workspace IDs from the D1 export. Clone each one through the public API origin:
When Git asks for a password, enter a Workspaces API key. The username is ignored. Each clone contains that workspace’s full version history. The key owner must have read access to the workspace. One organization member cannot clone another member’s private workspace. Confirm that your backup list contains every workspace ID from D1.

Copy both R2 buckets

The usercontent bucket stores text objects at text/<workspace-id>/<blob-sha>. The asset bucket stores objects at <workspace-id>/<content-hash>. Read those values from D1, then fetch each object:
Current wrangler has no r2 object list command. For many objects, use the Cloudflare dashboard or an S3-compatible client instead of one command per key.

Restore D1 metadata

Do not replay a raw wrangler d1 export file. The table order can break foreign-key checks. Current wrangler also has no d1 import command.
1

Create a fresh D1 database

Put the new database ID in both Worker configs. Keep the app and usercontent database_name and database_id values equal.
2

Apply the source migrations

From apps/rooms, run:
3

Prepare the data-only import

Keep only INSERT statements from the backup. Remove every d1_migrations row. Order the inserts by table:
Save the result as data-only-parent-first.sql outside the repository.
4

Import the rows

5

Check row counts

Compare the result with the source backup. d1 execute --file can cancel at a confirmation prompt while exiting with status 0, so do not skip this check.
Deploy both Workers after the database IDs change. Then open a known workspace, check its current and older versions, open a comment and reply, make a new edit, and compare a living raw URL, an exact-version raw URL, and an uploaded asset with the backup.

Update both Workers

Back up the deployment before any update that adds a D1 migration.
  1. Check out the new named source tag and read its migration notes.
  2. Install the locked dependencies and build the browser app.
  3. Copy your resource values into the new app and usercontent config templates.
  4. Apply pending D1 migrations.
  5. Deploy the app Worker and the usercontent Worker from the same source tag.
Check /healthz on every origin, sign in through Access, open and edit a known document, check living and exact-version raw links, and test one API-key request and Git fetch.

Roll back safely

Cloudflare can roll a Worker back to a prior registered version. Roll back both Workers to versions from the same source commit, then repeat the update checks.
Each command asks you to choose a prior deployed version. Choose versions that carry the same source commit. The recovery drill tested rollback only when both versions used the same D1 schema. It did not test older code against a newer migrated schema. Before a migrating update, take a D1 backup. If you must return to older code, restore that pre-update D1 backup instead of running the older code on the changed database. Reviewed July 22, 2026. Maintained by the Plannotator documentation team.
Last modified on July 24, 2026