Connect your Cloudflare R2 bucket to Dreep

Create an R2 bucket, create an R2 API token with Object Read & Write scoped to that bucket, then enter the bucket name, region auto, your account endpoint and the token's keys under Settings, Storage in Dreep. Dreep writes and deletes a probe object to confirm access. From then on, originals and generated variants land in your bucket and delivery stays on Dreep URLs.

How a request flows

  1. Your app

    Presigns an upload with your API key

  2. Dreep upload URL

    Browser PUTs bytes to cdn.dreep.cloud

  3. Your R2 bucket

    Dreep writes the original server-side

  4. Dreep CDN URL

    Transforms on request, variant saved to R2

Your users never talk to R2 directly. Dreep holds the credentials and is the only client of the bucket.

Before you start

Four conditions decide whether the connection will succeed. Check them first; they account for most failed setups.

A Starter or Pro plan

BYOB needs an active paid subscription on the project ($2.99/mo Starter or $5.99/mo Pro). Free projects are rejected with BYOB_PLAN_UPGRADE_REQUIRED.

An empty project

Connect the bucket before the first upload. A project that already holds images or files can't be switched, so create a fresh project for the R2-backed workload.

One bucket per project, for good

Once connected, the bucket can't be changed for that project. Pick the bucket name and Cloudflare account you intend to keep.

Owner access in Dreep

The Storage tab in Settings is only shown to the project owner. Team members can't add or view the bucket credentials.

Set it up in six steps

Steps one to three happen in the Cloudflare dashboard, four to six in Dreep. Allow about ten minutes.

  1. 1

    Create the bucket

    In Cloudflare, open R2 Object Storage and create a bucket, for example acme-media. Leave public access off: Dreep reads with credentials and serves through its own URLs, so the bucket never needs an r2.dev or custom public domain.

  2. 2

    Create a scoped R2 API token

    On the R2 overview page, under Account Details, choose Manage next to API Tokens and create a token. Select Object Read & Write and limit it to the bucket from step one. Admin permissions are not needed.

  3. 3

    Copy the keys and your account endpoint

    Cloudflare shows the Access Key ID and Secret Access Key once, so save both. Your S3 endpoint is https://<ACCOUNT_ID>.r2.cloudflarestorage.com. Buckets created under the EU jurisdiction use https://<ACCOUNT_ID>.eu.r2.cloudflarestorage.com instead.

  4. 4

    Open Settings, Storage in Dreep

    Choose Connect Your Own Bucket and pick Cloudflare R2 as the provider. The form asks for the five fields in the table below.

  5. 5

    Connect and let Dreep probe the bucket

    Dreep writes a small object called __dreep_byob_probe_test__, deletes it again, and lists top-level prefixes. If all three calls succeed, the keys are encrypted with AES-256-GCM and the configuration is saved.

  6. 6

    Upload exactly as before

    Nothing changes in your code. The same API key, SDK calls and delivery URLs now read from and write to your R2 bucket.

The fields Dreep asks for

FieldWhat to enter for R2Notes
ProviderCloudflare R2Sets how the endpoint is handled.
Bucket Nameacme-mediaExact name, no r2:// prefix or URL.
RegionautoR2 uses auto. The field is required, so type it in.
S3 Endpoint URLhttps://<ACCOUNT_ID>.r2.cloudflarestorage.comRequired for R2. Account ID is on the R2 overview page. Don't append the bucket name.
Access Key IDFrom the R2 API tokenNot your Cloudflare API token value.
Secret Access KeyFrom the R2 API tokenShown once by Cloudflare.

Why Object Read & Write is enough

Cloudflare's Object Read & Write permission allows reading, writing and listing objects in the buckets you choose. That covers every S3 call Dreep makes against a connected bucket:

S3 operationWhen Dreep uses it
PutObjectProbe on connect, every original, every generated image or video variant.
GetObject / HeadObjectServing originals and cached variants, checking whether a variant already exists.
DeleteObjectRemoving the probe object, deleting an asset you delete in Dreep.
ListObjectsV2Discovering top-level prefixes when you test the connection.

Admin Read & Write would also let Dreep create and delete buckets, which it never does. Keep the token on Object Read & Write and scoped to one bucket.

What Dreep writes to your bucket

Originals and every variant Dreep generates are stored in R2, so the bucket is a complete copy of the project's media. The layout is shown so you know what to expect; treat it as Dreep-managed and don't rename or move objects under these prefixes.

PrefixContents
projects/<project-slug>/<folder-path>/originals/<asset-id>/<filename>The uploaded file, after any upload-time transform you requested.
transforms/<content-hash>/<params-hash>.<ext>Variants generated on first request (resized images, converted formats, video renditions), reused on later requests.

Your upload code doesn't change

BYOB is a project setting, not an SDK option. The presign, PUT and confirm flow below is identical on Dreep storage and on R2; only where the bytes end up is different.

import { Dreep } from "dreep";

const dreep = new Dreep({ apiKey: process.env.DREEP_API_KEY });

// Same call as before BYOB. The bytes now land in your R2 bucket.
const upload = await dreep.presignUpload({
  filename: "listing-photo.jpg",
  contentType: "image/jpeg",
  sizeBytes: 2_480_000,
  folder: "listings/2026",
});

// Hand upload.uploadUrl to the browser, which PUTs the file, then:
const asset = await dreep.confirmUpload({ id: upload.id });

dreep.url(asset, { width: 800, format: "webp", quality: 80 });

Do I need a CORS rule on the bucket?

Not for uploads. Browsers PUT to the upload URL Dreep returns, which lives on cdn.dreep.cloud, and Dreep forwards the bytes to R2 from its own servers. The browser never makes a cross-origin request to r2.cloudflarestorage.com, so R2's CORS policy is never consulted. You only need one if your own front end talks to the bucket directly, outside Dreep.

Troubleshooting connection errors

These are the BYOB error codes Dreep returns, what triggers each one, and the fix.

ErrorCauseFix
BYOB_CONNECTION_FAILEDThe probe write or delete was refused: wrong keys, a token without write access, or a token scoped to a different bucket.Recreate the token with Object Read & Write on this bucket and paste both keys again. Check the endpoint uses your account ID.
Bucket not foundThe endpoint is correct but no bucket with that name exists in the account or jurisdiction.Check the spelling, and use the .eu. endpoint for EU-jurisdiction buckets.
BYOB_PLAN_UPGRADE_REQUIREDThe project is on Free or its subscription isn't active.Upgrade the project to Starter or Pro, then connect again.
BYOB_PROJECT_NOT_EMPTYThe project already has images or files stored.Create a new project and connect R2 before the first upload.
BYOB_ALREADY_CONFIGURED_IMMUTABLEA bucket is already connected to this project, and it can't be swapped.Keep the existing bucket, or use a new project for a different bucket.
BYOB_STORAGE_UNREACHABLEConnection worked, but a later read or write failed: token revoked or rotated, bucket deleted, permissions narrowed.Restore the token's Object Read & Write access to the bucket. Don't delete the bucket while the project uses it.

Already have files in this bucket? Import them

BYOB and import do different jobs. BYOB makes Dreep write new uploads and variants into your bucket. Import copies objects that already exist in a bucket into a Dreep project, so they become Dreep assets with Dreep URLs.

To import, open Integrations in the Dreep dashboard, connect Cloudflare R2 with an access key, secret, account endpoint and bucket name, browse the objects, choose what to bring over and a destination folder, and start the job. It runs in the background with progress and logs. It is a one-off copy, not an ongoing sync, so an Object Read only token is enough.

Who this is for

  • Teams already standardised on Cloudflare R2 who want media to stay in their own account.
  • Projects that need a full copy of originals and variants under their own retention and backup policies.
  • Apps that want Dreep's transforms and delivery without moving storage ownership to a vendor.

When Dreep is not the right fit

  • Existing Dreep projects with media already uploaded: the bucket must be connected while the project is empty.
  • Workloads on the Free plan: BYOB starts at Starter.
  • Serving objects already in the bucket as-is: BYOB covers new uploads; existing objects need the separate R2 import, which copies them into a Dreep project.

Frequently asked questions

Which R2 API token permission does Dreep need?

Object Read & Write, scoped to the one bucket you connect. It covers the PutObject, GetObject, HeadObject, DeleteObject and ListObjectsV2 calls Dreep makes. Admin permissions are not required.

What region do I enter for Cloudflare R2?

Enter auto. R2 doesn't use AWS-style regions, and the endpoint URL (https://<ACCOUNT_ID>.r2.cloudflarestorage.com) is what tells Dreep where the bucket lives.

Do I need to make the R2 bucket public?

No. Dreep authenticates with your token for every read and write and serves media through Dreep CDN URLs, so the bucket can stay private with no r2.dev or custom domain.

Can I connect R2 to a project that already has uploads?

No. BYOB has to be connected while the project is empty. Create a new project, connect R2, then point your app at the new project's API key.

Can I switch to a different bucket later?

Not on the same project. A connected bucket is permanent for that project, so choose the bucket and Cloudflare account you intend to keep.

Can Dreep import the objects already in my R2 bucket?

Not through BYOB: connecting R2 only changes where new uploads and variants are stored. Use the separate R2 import under Integrations in the dashboard, which copies the objects you choose into a Dreep project as a background job.

Related

Keep your media in your own R2 account

Create a project on Starter or Pro, connect your bucket before the first upload, and ship with the same SDK calls.