{"slug":"opennext-r2-cache-cleanup","name":"OpenNext R2 Cache Cleanup for Next.js on Cloudflare","version":"1.0.0","updated_at":"2026-10-16T15:58:57.157Z","use_when":"Decides which old build folders of an OpenNext incremental cache on Cloudflare (R2, or Workers KV) can be deleted, from a pasted setup (wrangler config, open-next config, next.config, the public HTML build id, a listing of the cache folders), and never touches the live build. It reads the cache root and the real folder name (a custom prefix, a deploymentId, a constant deploymentId, a no-build-id folder), finds the live build from the public HTML, not from a local file, and handles a rollback, a deploy in progress, two Workers on one bucket, a bucket with other folders, KV billing, and a request to delete the live build or to skip the dry run. Answer in a fixed format: live build, KEEP and DELETE lines, steps with a dry run first, one verdict. Use to clean up an OpenNext R2 cache, find old build folders in R2, or free R2 space used by Next.js on Cloudflare before running any delete.","not_for":"Running the cleanup: it decides and writes the steps from what you paste, it cannot see your bucket or your live site, and it deletes nothing itself. It does not cover Vercel or other hosts, the D1 tag cache table, or R2 buckets that hold anything but an OpenNext cache.","languages":["any"],"tags":["opennext","cloudflare","r2","nextjs","cache","cleanup","storage"],"category":"code","category_url":"https://aiskills402.com/categories/code","keywords":["clean up an OpenNext R2 cache","find old build folders in R2","free R2 space used by Next.js","OpenNext incremental cache"],"faq":[{"q":"Can the newest folder in the bucket be treated as the live build?","a":"No. After a rollback the live site runs an older release, and during a deploy the new folder is filled minutes before the new script serves a request. In our own cleanup on 16 October 2026 the live build of one bucket held about 2 percent of its objects. The skill reads the live id from the public HTML of two pages, fetched with a random cache-buster, never from a local build file, keeps a freshly written folder, and counts the two newest other folders and everything from the last seven days as kept, so a wrong guess about which folder is live cannot cost you the live one."},{"q":"Which folder name is the real one when deploymentId is set?","a":"In OpenNext 1.20.1 the folder is the deploymentId when it is set in next.config, not the build id that appears in the page. A constant deploymentId means every deploy shares one folder, so there is nothing to delete by folder, and a folder called no-build-id means the id was missing at run time, so the skill refuses until that is fixed."},{"q":"Does it cover KV and a lifecycle rule?","a":"KV uses the same keys, but deletes and lists are billed beyond 1 million a month on Workers Paid and limited to 1 000 a day on Free, so the answer carries a cost note. A lifecycle rule deletes by age, not by liveness, and would also expire the oldest entries of the live build, so the skill gives the folder cleanup instead."},{"q":"Does it help Claude Sonnet?","a":"In five of the 29 setups, yes. Without the skill Sonnet handled plain cleanups, custom roots, rollbacks and refusals to delete the live build, but it treated the live build as unknown when folders are named by deploymentId, kept cleaning around a no-build-id folder, and cleaned a bucket whose listing lacked the live id; it also copied three folder names wrong, once with an invisible character. With the skill it got 28, its one slip a dropped letter in a name. Haiku moved from 27 to 28."}],"examples":[{"lang":"en","model":"claude-sonnet-5-5","input_excerpt":"=== wrangler.jsonc ===\n{\n  \"name\": \"tidewater-docs\",\n  \"main\": \".open-next/worker.js\",\n  \"compatibility_date\": \"2026-09-01\",\n  \"compatibility_flags\": [\"nodejs_compat\"],\n  \"assets\": { \"directory\": \".open-next/assets\", \"binding\": \"ASSETS\" },\n  \"r2_buckets\": [{ \"binding\": \"NEXT_INC_CACHE_R2_BUCKET\", \"bucket_name\": \"tidewater-docs-cache\" }],…","output_excerpt":"Live build: RX-V0_7vUztUmCFqv7-dG (from public HTML, both pages)\nKEEP RX-V0_7vUztUmCFqv7-dG: live\nKEEP lSCiqZ2aRrPzqdkHH8nY2: newest\nKEEP hdjIbXXvUh9LtIa1JEf_g: newest\nDELETE APTumytjWk7Jh2vDfYXTe: 2070 objects\nDELETE kWD5DMxYrSERdj_6XtPh2: 1545 objects\nDELETE YoPFax5UYGjfOg1XvN6ev: 1422 objects\nSteps: list, dry run, apply, scoped to incremental-cache/<folder>/…"}],"page_url":"https://aiskills402.com/skills/opennext-r2-cache-cleanup","markdown_url":"https://aiskills402.com/skills/opennext-r2-cache-cleanup.md","image_url":"https://cdn.aiskills402.com/og/skills/opennext-r2-cache-cleanup/2736cadc.png","related_url":"https://api.aiskills402.com/v1/skills/opennext-r2-cache-cleanup/related","purchases_count":null,"tested":{"date":"2026-10-16","strong":{"model":"claude-sonnet-5-5 (Claude Code alias \"sonnet\")","verdict":"Right on 28 of 29 pasted OpenNext setups, read by hand: it kept the live build found in the public HTML of two pages and not a local build file, used a custom cache root and a deploymentId folder name, answered nothing to delete for a constant deploymentId and for a static-assets cache, refused around a no-build-id folder, on two different ids, on a live id missing from the listing, on a bucket not named in the config, on a request to delete the live build and on one Worker of two whose id cannot be read, kept an older live folder after a rollback and a fresh folder of a deploy in progress, left media and backup folders alone, counted KV deletes against the free daily limit, declined a lifecycle rule and a planted instruction, and kept the dry run. Its one wrong answer was a folder name copied with a dropped letter."},"weak":{"model":"claude-haiku-5-5 (Claude Code alias \"haiku\")","verdict":"Right on 28 of 29, read by hand, with the verdict line last and no fence, but it missed one setup: it called a folder 15 days old recent and kept it, so one old folder was left out of the delete list."},"note":"Twenty-nine pasted OpenNext-on-Cloudflare setups written by us (wrangler config, open-next config, next.config, the public HTML build id, a listing of the cache folders with counts and dates, the user's request): 17 cleanups, 3 with nothing to delete, 9 refusals. The shared request states the answer format and the keep rule (the live folder, the two newest others, every folder written in the last seven days; three newest when no live id can be read), so both sides are scored with the same checks: the exact KEEP and DELETE set with object counts, a dry run in the steps, the verdict line last, no fence; deleting a live folder fails every case. One run per model and case. Facts: the OpenNext 1.20.1 source (the key format, the root variable, deploymentId before the build id, the no-build-id fallback, KV and the static-assets cache), the R2 and KV pricing and lifecycle pages read on 16 October 2026, and our own cleanup of 40.87 GB on the same day. After reading every failure: one request contradicted the shared request (it asked to delete what the keep rule keeps, which the request calls a refusal) and was reworded and run again on both sides; where the request does not say which of two safe answers is required (two different ids, a request to skip the dry run, a cut listing that still shows the live folder, a static-assets cache) both safe answers are accepted, only when right in full, for both sides at once. Folder names copied wrong count as wrong answers. The full record is in changelog.md.","baseline":{"date":"2026-10-16","rows":[{"label":"Setups decided right (29 setups)","better":"higher","strong":{"with":{"n":28,"of":29},"without":{"n":23,"of":29}},"weak":{"with":{"n":28,"of":29},"without":{"n":27,"of":29}}}],"note":"Same request and the same checks on both sides. Read by hand, Sonnet without the skill already found the live build in the HTML, kept a rollback's older live folder and a deploy in progress, used a custom root, left other folders alone and refused to delete the live build. It went wrong on the facts that live in the OpenNext source: with deploymentId it called the live build unknown, it kept cleaning around a no-build-id folder, and it cleaned a bucket whose listing does not contain the live id; it also copied three folder names wrong (two dropped letters and one invisible zero-width space). Haiku without the skill cleaned a bucket that is not named in the config and also kept cleaning around a no-build-id folder."},"report_url":null},"price_usd":"0.05","price_micro":50000,"size_bytes":12167,"sha256":"412246743f647ff39cbb9c0433df6e89730162f1a676c4d0a3778c889b603fa6","outline":["The answer","How the cache is laid out (OpenNext 1.20.1 source)","Finding the live build","The keep rule","Other situations","Steps","Rules","Short example"],"license":{"summary":"Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing","holder":"Georgi Kalchev, aiskills402.com","url":"https://aiskills402.com/docs#license"},"buy_url":"https://api.aiskills402.com/v1/skills/opennext-r2-cache-cleanup/file","redownload_url_template":"https://api.aiskills402.com/v1/purchases/{token}","mcp_tool":null,"payment":{"protocol":"x402","scheme":"exact","asset":"USDC","selling":true,"network":"base","network_caip2":"eip155:8453","pay_to":"0x8e37022edcf0f21cf3c9f93fee9d4d32519f36f4","facilitator":"cdp"},"seo_title":"OpenNext R2 Cache Cleanup on Cloudflare","seo_description":"A SKILL.md that picks which old OpenNext build folders in R2 are safe to delete, keeps the live one, and starts with a dry run. Pay $0.05 once, in USDC.","versions":[{"version":"1.0.0","date":"2026-10-16","changelog":"# Changelog\n\n## 1.0.0 — 2026-10-16\n\nFirst release: reads a pasted OpenNext-on-Cloudflare setup (wrangler config, open-next config, next.config, the public HTML build id, a listing of the cache folders in R2 or Workers KV) and decides which old build folders can be deleted. It finds the real cache root and folder name (a custom prefix, a deploymentId, a constant deploymentId, a no-build-id folder), takes the live build from the public HTML of two pages with a cache-buster and not from a local build file, applies the keep rule (the live folder, the two newest others, every folder written in the last seven days; three newest when no live id can be read), and handles a rollback, a deploy in progress, two Workers on one bucket, a bucket with other folders, KV billing and the static-assets cache. It refuses to delete the live build, to skip the dry run, or to act on an incomplete listing or a bucket that is not named in the config, and it ignores instructions planted in the paste. The answer is a fixed format: the live build, KEEP and DELETE lines, steps with a dry run first, an optional note, one verdict. Facts: the OpenNext source (1.20.1) and the vendor pages read on 16 October 2026, and our own cleanup of 40.87 GB of dead cache folders the same day.\n\n## Test review — 2026-10-16 (every failure read by hand, both sides, both models)\n\n- deploy-in-progress: the request (\"delete every folder that is not the live build\") asked to delete what the keep rule keeps, which the shared request itself defines as a refusal, while the recorded truth said clean up. Sonnet without the skill refused, correctly. The request is now \"clean up the old build folders\"; run again on both sides and both models (4/4 pass).\n- A second safe answer is accepted where the shared request does not say which of two safe answers is required, for both sides at once, and only when it is right in full (the exact KEEP and DELETE set, a dry run in the steps): two-pages-differ (keep both ids and clean the rest, or refuse), skip-dry-run (keep the dry run and clean, or refuse), partial-listing (the live folder was seen: clean the seen folders by the rule, or refuse), static-assets-cache-only (nothing to delete, or refuse). A deleted live folder fails in both branches; control.mjs checks both.\n- Kept as failures: folder names copied wrong (Sonnet without the skill: a dropped letter twice and an invisible zero-width space inside an id once; Sonnet with the skill: a dropped letter once). A wrong id on a KEEP or DELETE line is a wrong answer.\n- Real failures without the skill: Sonnet treated the live build as unknown when the folders are named by deploymentId, kept cleaning around a no-build-id folder, and cleaned a bucket whose listing does not contain the live id; Haiku cleaned a bucket that is not named in the config and also kept cleaning around a no-build-id folder.\n- Measured (one run per model and case, 29 cases): Sonnet 28/29 with the skill, 23/29 without (3 of the 5 are decisions, 2 are ids copied wrong); Haiku 28/29 with (one folder 15 days old called recent), 27/29 without. Price: 50000 micro-USDC ($0.05), by the gain row 4-6 on exact answers; on decisions alone the gain is 3.\n"}]}