What is live today: a declared store is created and bound.
env.BUCKET is real in
your Worker, env.IMAGES is real when you ask for it, and both are removed with the site —
an empty bucket is. A bucket that still holds objects is kept, and the deletion receipt
says so — see “It goes when the site goes” below.What you get, and what we never touch
One bucket belongs to one site. It is created the first time you publish a build that declares it, and it is bound into that site’s Worker. Your code is the only thing that ever writes to it or reads from it — nothing on our side opens it, lists it, or hands its contents to anyone. It is private. There is no public URL for an object in your bucket. A visitor sees a file because your own route read it and returned it, which means the rules about who may see what are yours and are written in your code. It goes when the site goes — once it is empty. Deleting the site removes the bucket. A bucket that still holds objects is not removed: R2 refuses to delete a non-empty bucket, and we deliberately do not enumerate and erase your bytes on our own initiative. Such a bucket is retained and reported in the deletion receipt rather than silently dropped, so you can see exactly what is left. Emptying it is your code’s job today. If you want the bytes gone with the site, delete the objects throughenv.BUCKET before you delete the site. A platform-side drain — one that
empties the bucket for you as part of deletion — is not built yet; when it ships, this page
says so and the sentence above becomes unconditional.
Declaring it
Two lines inpages.deploy.json:
name is the only part you choose. Which bucket it points at is ours and is not
settable: it is derived from your site’s id, it is what our own teardown finds the bucket by,
and it is checked on every publish before your Worker goes up. That is what makes it
impossible for one site’s binding to be pointed at another site’s files.
Five names are already taken and are refused by name if you ask for one: DB (your
site’s database), ASSETS (your own static files), IMAGES (the transform binding), and
GOOSY_API_KEY / GOOSY_API_BASE (the key your site calls our API with, and the API origin
it calls). They share one namespace with your binding, so a collision would not fail — it
would quietly give your code the wrong handle.
Writing and reading — a complete Astro route
An upload endpoint and the route that serves the file back:Resizing on the way out — env.IMAGES
With "images": true your Worker can transform bytes it already holds, without them being
reachable at a URL first:
/media/guests/abc?w=320 serves a 320-pixel WebP of the same object, and the original
stays where you put it.
Limits and cost
Related
- The deploy contract — every field of
pages.deploy.json. - Site and project databases —
env.DB, for rows rather than bytes. - Errors and refusals — the shared error envelope.