Resources
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.
| Resource | For | Holds | Name | Created by |
|---|---|---|---|---|
| Worker | each app | the code, one version per deploy | <slug>-<app>-<context> | the deploy |
| R2 bucket | apps/web | uploaded files | <slug>-web-<context> | the resources step |
| Hyperdrive configuration | apps/web | the pooled connection to Postgres | <slug>-web-<context> | the resources step |
| D1 database | apps/site | the @nuxt/content dump | <slug>-site-<context> | the resources step |
| Postgres | apps/web | application data | yours to choose | you, 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:
- It reads each resource name through varlock.
- It finds each resource by that name, and creates the one that's missing.
- It updates
apps/web's Hyperdrive configuration fromDATABASE_URL. On a pull request, it creates or reuses that pull request's Preview Database and its own configuration instead. - 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:
| Resource | Command |
|---|---|
| R2 bucket | pnpm exec wrangler r2 bucket delete <slug>-web-<context> |
| D1 database | pnpm exec wrangler d1 delete <slug>-site-<context> |
| Hyperdrive configuration | pnpm exec wrangler hyperdrive delete <id> |
| Worker | pnpm 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
- Deploy pipeline — which event deploys which context, and what each application binds
- Deploying development — the repository's
one-time Cloudflare setup, and the first push to
development - Adding pull request previews — the first pull request, which creates preview's Worker and resources
- Adding staging — the optional
stagingcontext, whose first push creates its resources - Going to production — the first push to
main, which creates production's resources
