Skip to content

Latest commit

 

History

History
77 lines (50 loc) · 7.61 KB

backup-restore.md

File metadata and controls

77 lines (50 loc) · 7.61 KB

Backup and restore

Native transport is included in backups for each channel when the NATIVE_TRANSPORT binding is present. Maintenance mode stops its container and blocks new operations and state callbacks before taking the snapshot. Active operations must finish before backup can proceed.

Restoring intentionally clears native SSH keys, server trust, Tailscale enrollment and session authority instead of reviving old credentials or previously revoked grants. Re-enroll Tailscale, install the newly generated SSH public key on approved servers, and approve server trust again after restoring. Saved tasks and files are restored normally; a native session is not resumed from backup. Backups predating this transport feature contain no transport snapshot. Restore those into a fresh transport namespace, not over an already enrolled one.

Conversations, tasks, memory, usage history, file preparation jobs, and channel computer layer indexes live in SQLite-backed Durable Objects. A complete backup needs their snapshot plus D1, every R2 object, the private Wrangler config, and recovery secrets. Keep all parts under one backup ID. This cross-account procedure still needs a live Cloudflare restore rehearsal before relying on it for disaster recovery. Do not delete the original Worker or Durable Object namespaces until that rehearsal succeeds.

Stop writes and snapshot Durable Objects

Wait for running tasks and file preparation to finish. In your private wrangler.jsonc, set BACKUP_MAINTENANCE to "1" in vars, then deploy that configuration. Maintenance returns 503 for ordinary app requests, skips scheduled jobs, and defers Durable Object alarms. Keep this mode on until D1 and R2 are copied. The backup API stops each channel computer after saving it; if a save fails, the backup stops without publishing a complete index.

With the original recovery JSON generated by setup, run:

npm run backup:objects -- https://<your-worker-host>/ ~/.config/onehelm/<worker>-recovery.json

The command prints the backup ID as soon as it has a plan. Record that ID and the final index_key. It uses the recovery file's BACKUP_TOKEN to snapshot each named Durable Object into the installation's R2 bucket, one bounded page per request. It writes backup/<backup_id>/index.json only after all planned objects have snapshot manifests. The recovery file is a secret; keep it out of the repository and command output. If the command is interrupted, keep maintenance on and resume the same backup ID:

npm run backup:objects -- https://<your-worker-host>/ <recovery-file> <backup_id>

Each completed page is reused. If the source workspace changed while the command was stopped, begin a new backup ID after writes have stopped again.

Back up the D1 SQL export, every R2 object including backup/<backup_id>/, the private wrangler.jsonc, and the recovery JSON together. D1 rows and Durable Object indexes refer to R2 object keys. Store the backup encrypted.

D1

While maintenance remains on and wrangler.jsonc points at the installation, export the remote database:

mkdir -p backups
npx wrangler d1 export DB --remote --output backups/onehelm.sql

To restore, create a new empty D1 database in the target account, update the D1 ID in a copy of wrangler.jsonc, then import the SQL export with npx wrangler d1 execute DB --remote --file backups/onehelm.sql. An exported schema already includes its tables and migration tracking; do not apply fresh migrations before importing it. After import, run npx wrangler d1 migrations list DB --remote to see whether newer migrations are pending. Do not run npm run setup against the restore target: it creates and migrates a new database before import.

R2

Use Cloudflare's S3-compatible R2 API and rclone to copy the bucket to encrypted storage. Give the R2 credentials access only to this bucket. 1Helm stores HTTP and custom metadata on R2 objects; preserve it along with the bytes. For a new, empty snapshot directory on a Linux or macOS filesystem with extended attributes, use:

rclone copy --metadata r2:<bucket> backups/r2/<bucket>
rclone check --download r2:<bucket> backups/r2/<bucket>

Keep that directory on encrypted storage that preserves extended attributes. The local rclone backend cannot store user metadata on Windows, and some filesystems lack extended attributes; in those cases choose an encrypted, metadata-capable rclone destination instead of a plain local directory. Keep a separate snapshot for each backup. Rclone's metadata guide and local backend guide explain these requirements.

Restore to a new empty R2 bucket with rclone copy --metadata backups/r2/<bucket> r2:<new-bucket>, then set that bucket name in the restored wrangler.jsonc. Run rclone check --download backups/r2/<bucket> r2:<new-bucket> to compare object contents, and compare representative source and restored object metadata with rclone lsjson --metadata --stat r2:<bucket>/<sample-key> and the corresponding command on r2:<new-bucket>/<sample-key>. Check the object count before switching the Worker. A content check alone does not verify metadata. Confirm backup/<backup_id>/index.json is present in the new bucket before restoring Durable Objects.

Durable Objects

Use the same source commit that made the backup; upgrade only after verifying the restored installation. In the target account's private wrangler.jsonc, use a new Worker with the same six Durable Object class bindings as the source. Set BACKUP_MAINTENANCE and BACKUP_RESTORE_MODE to "1" in vars; point D1, R2, host, and verified email sender at the target resources. Deploy with the original recovery JSON, including VAULT_KEY and BACKUP_TOKEN, using npx wrangler deploy --secrets-file <recovery-file>. A fresh deployment builds the computer image and creates the empty Durable Object namespaces. Then run:

npm run restore:objects -- https://<new-worker-host>/ <recovery-file> <backup_id>

Restore keeps ordinary requests and alarms paused. The command verifies object identities, SQL schemas, page checksums, and row counts before clearing each destination object's rows. It then applies pages in bounded requests and rebuilds the memory search index. If interrupted, rerun the same command while maintenance stays on; each object resumes at its stored step and a page retry is safe. After every object reports success, remove both maintenance vars and redeploy so Durable Objects restart on restored state. Verify the owner account, conversations, Library files, a saved workspace file, schedules, and one computer task before treating the target as recovered.

An ordinary source upgrade in the same Worker and namespaces keeps Durable Object storage; do not delete or rename those namespaces. Cloudflare's SQLite Durable Object point-in-time recovery can recover an individual object to a point in the last 30 days within its existing namespace, but it is not an independent cross-account export.

Keep the same owner email and app host if you want existing client connections to continue working. If the host changes, connect clients to the new HTTPS host and expect old shared URLs to need updating.

Cloudflare D1 Time Travel can help with recent database mistakes, but it does not replace an independent D1 and R2 backup. See Cloudflare's D1 export reference and R2 rclone guide.