Bring your own bucket
Bring your own bucket
Point a workspace at storage you already pay for, instead of the shared bucket.
Set up your bucket #
A workspace admin runs the connect wizard from the workspace settings page. Three steps on the Cloudflare dashboard, then verify before anything is saved.
1. Create an R2 bucket
Dashboard → R2 → Create bucket. Any name that follows Cloudflare's rules works (3–63 characters; lowercase letters, digits, hyphens). If the bucket is in a jurisdiction (eu or fedramp), pick that same jurisdiction in the wizard.
2. Create a bucket-scoped API token
R2 → Manage API Tokens → Create API Token, permission Object Read & Write, scoped to that one bucket. You get an Access Key ID, a Secret Access Key, and your account ID.
Scope the token to one bucket, not the whole account. uploads.sh encrypts the key pair, but a bucket-scoped token still limits the blast radius if anything goes wrong.
3. Optional: custom domain for public reads
Bucket → Settings → Public access → Custom Domains, connect a domain on a Cloudflare zone in your account, then paste the URL (for example https://media.example.com) into the wizard as the public base URL. See the serving matrix below for what this buys you.
Enter the account ID, bucket name, and key pair, then run verify. It checks the inputs, authenticates against the bucket, and round-trips a test object. Only a passing verify can save — and saving never changes where uploads go. The saved bucket sits in your settings page as "Saved · not in use" until you click Use this bucket, so you can connect and verify at any time without any risk to what's already uploading.
Switching is instant and reversible, even for a workspace that already has files: existing files keep resolving from wherever they already are, and only new uploads move to the newly active bucket. File-size and video-size limits still apply on a BYO bucket; the storage-quota budget does not.
Serving: custom domain vs signed-only #
| Public access | How files are served | Trade-off |
|---|---|---|
| Custom domain | Direct public URL under your domain. | Recommended — needs a domain on a Cloudflare zone in your account. |
| No public URL | Signed URLs, generated on demand. | Fine for viewing; GitHub embeds won't render (see below). |
r2.dev managed URL | — | Not supported. The wizard rejects r2.dev base URLs — connect a custom domain, or save without a public URL for signed-only access. |
| Files uploaded before switching | Keep serving from wherever they already were. | No migration, no broken links. Switching a workspace's active bucket only changes wherenew uploads land — existing files and their published URLs don't move. |
What's different #
- GitHub embeds. Shared-bucket images get an embed host that GitHub's Camo proxy revalidates after overwrite. That twin doesn't extend to a custom domain, so a BYO image without a public URL (signed-only) won't render inline in a GitHub comment. The file still uploads and still has a share page.
retentionDaysauto-cleanup. Age-based retention walks a prefix on the shared bucket. A BYO bucket has no shared prefix, so automatic retention isn't available — delete old files yourself if you need that.- Storage quota. On a BYO bucket the plan's storage budget doesn't apply (you pay Cloudflare for the bucket). File-size and video-size upload limits still do.
Data ownership & switching back #
The bucket and everything in it are yours. uploads.sh stores only encrypted credentials and a pointer to the bucket — never a copy of your files.
Switching back to storage.uploads.sh never touches your objects. Files you uploaded while your bucket was active keep resolving from it — only new uploads move to the shared bucket. Switching to your bucket again later is the same instant, reversible switch in the other direction.