cf-backup
Skill1 starsChanged 6 days agoMITView on GitHub
Back up a Cloudflare account's D1 databases and KV namespaces to a local timestamped folder without writing anything to Cloudflare. Exports each D1 database to .sql, saves every KV key with its value (base64 for binary), metadata and expiration in files `wrangler kv bulk put` can restore, writes a manifest with the sha256 of every file plus a RESTORE.md with the exact restore commands (never runs them), and can verify or prune backups later. Use for "back up my D1 database", "export my KV", "backup Cloudflare data", "snapshot my Worker's data", "before I run a migration", before a bulk delete or schema change, or when restoring from one of these backups.
In this repository25 other files it gives its agents.
- bash-checkpointSkill
- brave-cdpSkill
- cf-pages-deploySkill
- claude-md-doctorSkill
- claude-safety-auditSkill
- claude-sandboxSkill
- claude-token-auditSkill
- cws-extension-checkSkill
- env-doctorSkill
- event-recap-videoSkill
- freelance-proposalSkill
- gif-makerSkill
- itch-html5-packageSkill
- link-preview-checkSkill
- mcp-auditSkill
- package-guardSkill
- pwa-checkSkill
- read-guardSkill
- readme-upgradeSkill
- release-notesSkill
- secret-commit-guardSkill
- session-handoffSkill
- site-qaSkill
- thumbnail-makerSkill
- visual-diffSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 23
Read-only towards Cloudflare.
- Very specific: line 27
Before a migration: back up, run verify, then migrate.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
cf-backup
scripts/cf-backup.mjs is zero-dependency Node 22+ and uses the user's existing wrangler login. Run npx wrangler whoami first. If it says not logged in, ask the user to run npx wrangler login.
node cf-backup.mjs --dry-run # ALWAYS first: databases, namespaces, key counts, request estimate
node cf-backup.mjs # everything, into ~/cf-backups/cf-backup-<UTC stamp>/
node cf-backup.mjs --d1 my-db --kv orders,sessions # only these (names, titles or ids)
node cf-backup.mjs --out D:\backups --keep 7 # then delete older backups this tool made there
node cf-backup.mjs --account <id> # login sees several accounts (or set CLOUDFLARE_ACCOUNT_ID)
node cf-backup.mjs verify <backupDir> # re-hash every file, replay the SQL in SQLite, decode every KV entry
node cf-backup.mjs prune <outDir> --keep 7 --dry-run
node cf-backup.mjs --local --cwd <project> --d1 my-db --kv <namespace-id> # wrangler dev stateExit codes: 0 ok, 1 finished with problems (listed, backup marked incomplete), 2 refused or bad usage.
Rules
- Read-only towards Cloudflare. Never run a RESTORE.md command unless the user asks to restore. Then show the command, and restore into a new database or namespace unless they say otherwise.
- Backups are user data. Never
cat, print or paste the SQL or KV files, and never commit them. Report counts and sizes only. To check one, runverify. - Tell the user before the first D1 export: a D1 database can't serve queries while its export runs (wrangler's own warning). Small databases take seconds; schedule big ones for a quiet time.
- The script refuses an
--outinside a git work tree. Pass--allow-gitonly if the user confirms that folder is git-ignored. Each backup also gets a.gitignorecontaining*. - Before a migration: back up, run
verify, then migrate.--keeponly prunes after a complete run, only folders with a cf-backup manifest, and only for the same account.
What a backup contains
cf-backup-20261115-051528Z/
.gitignore "*" plus a do-not-commit warning
manifest.json counts, sizes, sha256 per file, timestamps, D1 row counts, Time Travel bookmark, failed keys
RESTORE.md exact restore commands, not run
d1/<db>.sql wrangler d1 export output
kv/<title>__<id8>/part-0001.json JSON array, one {key,value,base64?,expiration?,metadata?} per line, ≤64 MiB per fileHow it reads
- REST API with the token from the wrangler login (
wrangler auth token, never printed), orCLOUDFLARE_API_TOKEN. It falls back to wrangler subcommands (--via wrangler) if no token is available. - KV: keys are listed 1,000 per request. Values come from bulk reads, 100 per request. Binary values come back from those as U+FFFD, so the script re-reads them one at a time, byte for byte.
--exactreads every value that way. - Rate limits: Cloudflare allows 1,200 requests per 5 minutes per user, shared with the dashboard. The script defaults to 3 requests/s (
--rps) with 6 in flight (--concurrency). On a 429 it waits forRetry-After; on 5xx and network errors it retries with exponential backoff (8 tries). 3,000 text keys take about 33 requests. 3,000 binary keys take about 17 minutes. - D1:
wrangler d1 export <db> --remote --output … -y. The dump is replayed into in-memory SQLite (node:sqlite) to count rows, and the Time Travel bookmark is recorded.
Restoring (from RESTORE.md)
npx wrangler d1 create my-db-restored
npx wrangler d1 execute my-db-restored --remote --file "d1/my-db.sql" -y
npx wrangler kv bulk put "kv/orders__3f9a1c2e/part-0001.json" --namespace-id <id> --remote- The SQL creates its tables, so it fails on a database that still has them. Restore into a new one and repoint the binding.
kv bulk putoverwrites keys with the same name and leaves other keys alone. It rejects entries whose expiration has already passed, so remove those first.- Within Time Travel's window (7 days on Free, 30 on Paid),
wrangler d1 time-travel restore <db> --bookmark=<from RESTORE.md>rolls the live database back without the file. It replaces the current data.
Limits
- wrangler can't export D1 databases that contain virtual tables (FTS5).
- KV listing is eventually consistent. Keys deleted mid-backup are counted as "deleted while reading". Keys that fail are listed in
manifest.jsonunderfailedKeys, and the run exits 1. wrangler kv bulk putrefuses metadata that isn't a JSON object. Such keys go tononobject-metadata-*.json, with a curl restore command.- The wrangler transport can't read keys that start with
-(yargs treats them as flags). The REST transport can.--localruns one wrangler process at a time because parallel ones lock miniflare's SQLite.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

