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's settings page. It walks through three steps on the Cloudflare dashboard, then verifies each one before saving anything.
1. Create an R2 bucket
Dashboard → R2 → Create bucket. Any name that follows Cloudflare's bucket-naming rules works (3–63 characters; lowercase letters, digits, and hyphens). If you created the bucket in a jurisdiction (eu, or fedramp — available to eligible Cloudflare accounts), pick that same jurisdiction in the wizard so verification and serving target the jurisdiction-specific endpoint.
2. Create a bucket-scoped API token
R2 → Manage API Tokens → Create API Token, permission Object Read & Write, scoped to that one bucket only. This yields an Access Key ID, a Secret Access Key, and your account ID.
Scope the token to the one bucket, not your whole account. uploads.sh stores the key pair encrypted, but a bucket-scoped token keeps the blast radius to that bucket if anything ever goes wrong on either side.
3. Optional: connect a custom domain for public reads
Bucket → Settings → Public access → Custom Domains, and connect a domain on a Cloudflare zone in your account. Cloudflare provisions the DNS record and certificate; paste the resulting URL (for example https://media.example.com) into the wizard as the public base URL. See the serving matrix below for what this step buys you.
Paste the account ID, bucket name, and key pair into the wizard's form, then run verify. It checks the shape of what you entered, confirms the token can authenticate against the bucket, round-trips a test object to prove the token is read and write, and (for a first-time attach) confirms the bucket is empty unless you explicitly say to adopt its existing contents. Only a passing verify lets you save.
Connecting is only available at workspace creation or on an otherwise-empty existing workspace — there's no migration path yet for moving a workspace that already has files onto a different bucket, because every published URL for those files would break. Upload limits (file size, video size) still apply on a BYO bucket even though the storage-quota budget doesn't.
Serving: custom domain vs signed-only #
| Public access | How files are served | Trade-off |
|---|---|---|
| Custom domain | Direct public URL under your domain, same as a normal share link. | Recommended — needs a domain on a Cloudflare zone in your account. |
| No public URL | Signed URLs, generated on demand. | Works for viewing files, but GitHub embeds won't render (see below). |
r2.dev managed URL | — | Not supported right now. The wizard rejects an r2.dev public base URL outright — connect a custom domain instead, or save without a public URL for signed-only access. |
What's degraded #
- GitHub embeds. Images on the shared bucket get an embed-friendly twin host that GitHub's image proxy (Camo) revalidates cleanly after an overwrite. That twin doesn't extend to a custom domain, so an image on a BYO bucket without a public URL — or served only through signed URLs — won't render inline in a GitHub comment. It still uploads and still gets a share page; only the inline-image rendering is affected.
retentionDaysauto-cleanup. The age-based retention sweep walks a prefix on the shared bucket. A BYO bucket has no shared prefix to confine that walk to, so automatic retention isn't available — delete old files yourself if you need that.- Storage quota. While a workspace is on a BYO bucket, the plan's storage budget doesn't apply (you're paying Cloudflare directly for that bucket) — but file-size and video-size upload limits still do.
Data ownership & disconnecting #
The bucket and everything in it are yours. uploads.sh stores only the encrypted credentials and a pointer to the bucket in the workspace record — never a copy of your files' bytes anywhere else.
Disconnecting from workspace settings never touches your objects. It removes the stored credentials and points the workspace back at the shared bucket; your bucket, and everything in it, is left exactly as it was. Re-running the connect wizard afterward is a fresh attach, same as the first time.