Deployment

Resources

The Cloudflare resources each Runtime Context runs on, what creates them, and how to see and delete them.

Every Cloudflare resource your project runs on is named <slug>-<app>-<context>, and the first run that deploys a context creates it. There's no provisioning step to run: every run that deploys, pull requests included, finds its context's resources by name and creates the one that's missing.

What each context gets

<context> is the Runtime Context as it is — development, preview, staging or production.

ResourceForHoldsNameCreated by
Workereach appthe code, one version per deploy<slug>-<app>-<context>the deploy
R2 bucketapps/webuploaded files<slug>-web-<context>the resources step
Hyperdrive configurationapps/webthe pooled connection to Postgres<slug>-web-<context>the resources step
D1 databaseapps/sitethe @nuxt/content dump<slug>-site-<context>the resources step
Postgresapps/webapplication datayours to chooseyou, at your provider

preview's Worker serves no deployment of its own. The first pull request's run creates it, and every pull request is a Preview on it, bound to preview's own bucket and content database. Its apps/web Preview binds a Preview Database instead of preview's pool. Pull request previews has the rest.

The slug is PROJECT_SLUG in .env.project.schema, and each application's .env.schema builds the names from it, so no value file holds one. Project identity is the full rule. Postgres is the one resource you create yourself, one per deployed context, at the Getting Started stage that adds the context.

How a run creates and finds them

Upsert the Cloudflare resources in .github/workflows/deploy.yml is the resources step. It runs before the build in every run that deploys, previews included:

  1. It reads each resource name through varlock.
  2. It finds each resource by that name, and creates the one that's missing.
  3. It updates apps/web's Hyperdrive configuration from DATABASE_URL. On a pull request, it creates or reuses that pull request's Preview Database and its own configuration instead.
  4. It hands the D1 and Hyperdrive ids to the build.

varlock-wrangler deploy and varlock-wrangler preview only bind what the step found, so a context's first run creates its resources and every later run finds them.

D1 is bound by id as well as by name, because Cloudflare refuses a D1 binding without an id in a Preview. That id is CLOUDFLARE_D1_DATABASE_ID, and the step is its only source: no value file holds it.

A new resource takes a name item in the application's .env.schema, its binding, and one find-or-create line in the step, in the shape of the ones already there. A new resource type also needs its token permission.

The Hyperdrive configuration

A Hyperdrive configuration is built from the database's credentials, so the step does more for it than find it. It looks the configuration up by name through Cloudflare's API, updates it from DATABASE_URL when it exists, and creates it when it doesn't.

No id is copied into a file, and rotating DATABASE_URL in the secret store reaches Hyperdrive on that context's next run. A second configuration with the same name — one made by hand, say — stops the step rather than letting it pick one.

Wrangler reads the host, port, user, password and database name out of DATABASE_URL, and percent-decodes them. Query parameters such as sslmode and channel_binding don't reach Hyperdrive, so paste the string your provider gives you unchanged: the migration step connects with all of it.

local

local never deploys, so nothing creates a -local resource, and today it needs none. Uploads land in .data/blob, the database is the Docker Postgres from Local setup, and the site reads its content locally.

The names still resolve — <slug>-web-local, <slug>-site-local. When a feature needs a resource that exists only on Cloudflare, AI Search for example, create it once under that name with wrangler <resource> create, and every developer on the project shares it.

The one exception is a stray Worker. <slug>-<app>-local exists only if someone deployed by hand without setting APP_RUNTIME_CONTEXT — Environment configuration says how that happens. Nothing uses it, so delete it.

See what exists

In the dashboard, go to Workers & Pages, select a Worker, and open Bindings. It names the bucket, database and pool that Worker's version is bound to. For a pull request's bindings, pick its Preview from the preview Worker's Preview selector first. From a terminal, signed in with pnpm exec wrangler login:

pnpm exec wrangler r2 bucket list
pnpm exec wrangler d1 list
pnpm exec wrangler hyperdrive list

Each lists every resource of its type in the account, so look for your slug when the account holds more than one project.

Delete a resource

Nothing deletes a resource automatically. Removing a binding, an application or a Runtime Context leaves its resources in place, data included. Delete them by hand once nothing needs them:

ResourceCommand
R2 bucketpnpm exec wrangler r2 bucket delete <slug>-web-<context>
D1 databasepnpm exec wrangler d1 delete <slug>-site-<context>
Hyperdrive configurationpnpm exec wrangler hyperdrive delete <id>
Workerpnpm exec wrangler delete <slug>-<app>-<context>

A Hyperdrive configuration is deleted by id, which wrangler hyperdrive list prints. Cloudflare refuses to delete a bucket that still holds objects, so empty it first. Postgres is yours to delete at your provider.

Delete a context's resources only once nothing deploys that context. A resource deleted while something still deploys it comes back, empty, on the next run.

A pull request's Preview and its Preview Database aren't resources in this sense. Closing the pull request deletes them — Pull request previews covers their lifetime.

Renaming your project

Changing PROJECT_SLUG changes every name, so the next deploy into each context creates new, empty resources and new Workers beside the old ones. The old ones keep their data until you delete them. Move what you need first, and rewrite the origins in your value files, because an origin carries the Worker name.

The token each resource type needs

The deploy authenticates with one Cloudflare token, and that token needs an Edit permission for every resource type the resources step creates. The Cloudflare API token lists them. A new resource type needs its permission added to the token first; a run without it fails at the resources step with Cloudflare's authentication error.

Going further

Copyright © 2026