Blog

Creating it once is the easy half

2026-06-14

An isometric illustration of a small timber house beside a neat stack of sawn lumber

Standing up a backend on Cloudflare means creating things in your account: a D1 database, a KV namespace, an R2 bucket, a Vectorize index, a Queue, a Workflow host, API tokens, secrets store entries, email routing rules, custom hostnames.

Doing that once is an afternoon of clicking. Doing it for dev, staging and prod is three afternoons and a spreadsheet. Doing it per feature branch is a different problem entirely — and the hard part is not the creating.

Fifteen kinds

The provisioning layer covers fifteen resource kinds — d1, kv, r2, ai, queue, workflows, email, secrets, tokens, hostnames, zones, turnstile, media, workers and environment configuration.

Each is addressed at the account level, so a provisioner both creates and tears down. The D1 provisioner’s own comment names the use case directly: standing up and tearing down per-environment databases, including ephemeral staging.

Provisioning is also deliberately separate from wiring. pithy add touches no Cloudflare account at all — it writes config and local state. pithy storage provision creates the bucket. Adding a capability should be free and reversible; creating billable infrastructure should be something you did on purpose.

Three problems that are not creation

Names that cannot collide. Every resource in an account shares a namespace, so a per-branch environment needs names nobody else will pick. Those names then have to be recomputed exactly at teardown, and read by a human in a dashboard listing that cannot be filtered — which is why truncating them against the wrong limit was worth fixing properly rather than approximately.

Tokens that cannot reach the wrong place. A branch environment holding a credential that works against production is a branch environment that can drop your production database. You will not find that out gradually. Tokens are minted per environment with the narrowest permission set that works — the secrets manager gets Secrets Store read and write, and nothing else.

Reclaiming everything when the branch dies. This is the one that actually costs money, and the one nobody notices.

The reaper, and why per-suite cleanup was wrong

Integration tests create real resources in a real account. Each suite cleaned up after itself, which sounds correct and is structurally broken.

Every live suite is guarded — it skips when credentials are absent. And a reaper registered in a suite hook does not run when that suite skips, because the test runner runs no hooks inside a skipped suite.

So the cleanup was gated behind exactly the condition that decided whether there was anything to clean up.

The second failure compounds it. A generic reaper knows how to reap a kind of resource, not which kinds exist — so a kind was only ever reaped where some suite happened to hand it a list-and-remove pair. Five such call sites covered the whole repository. Secrets Store entries, Queues and API tokens had none at all.

The result was eight pithy-int-secret-… entries sitting on a real Cloudflare account with nothing in the repository able to reclaim them, ever. Not a leak that alerts. Just resources, existing, billed, unreferenced.

The fix is one registry naming every reapable kind, swept once per run rather than per suite. Central, unconditional, and not dependent on which tests happened to execute.

Why orphans are the expensive part

Nothing complains about an orphaned resource. No page 500s. No error appears. Your only signal is a line on an invoice next to a name you do not recognize, and by then the branch it belonged to was deleted months ago.

That asymmetry is why creation is the easy half. A failed create is loud — the command errors, you fix it, you move on. A failed cleanup is silent and permanent, and the cost accrues quietly in an account nobody audits.

Per-branch environments make it worse in the obvious way. One left behind is a rounding error. A hundred branches over a year, each leaving a database, a bucket and three tokens, is a real bill and an account you can no longer read.

What this means if you build your own

Three things worth stealing, none of them about Cloudflare specifically:

  • Separate wiring from provisioning. Config changes should be free. Resource creation should be explicit.
  • Make teardown as central as creation. If cleanup lives next to the code that created something, it runs only when that code runs — which is exactly when it is least needed.
  • Assume the cleanup did not happen. A sweep that runs unconditionally and finds nothing costs seconds. One that runs conditionally and misses costs a year of storage on something nobody remembers.