Before you start
Set it up
1
Create the bucket
In the Cloudflare dashboard, open R2 object storage and choose Create bucket. Give it a name and keep the default location and the Standard storage class.
2
Create a token for this bucket only
On R2 object storage, open Overview and choose Manage next to API Tokens under Account Details. Create an Account API token with the Object Read & Write permission, applied to this bucket only.After you create it, Cloudflare shows the Access Key ID and the Secret Access Key once. Copy both, and your Account ID from the same page.
3
Set the variables
.env
<account-id>.eu.r2.cloudflarestorage.com. Without CLOUDFLARE_JURISDICTION, PostQueen asks the account-wide endpoint and reports the bucket as missing.4
Choose where media is read from
CLOUDFLARE_BUCKET_URL is the address stored with every file. Browsers load it, and several networks (Instagram, Facebook and Threads among them) download from it with their own servers. It has to be public HTTPS.Through PostQueen, the backend reads each file from the bucket and streams it back, cached for a year. It answers only for file names PostQueen itself writes: a random name with an image, video or audio extension. Even so, keep the bucket for PostQueen media only. The
/api in the address is required: the container’s nginx serves /uploads/ from the local disk, and only /api/ reaches the backend.5
Set the CORS policy
By default the browser uploads straight to the bucket, which is a cross-origin request the bucket has to allow. In the bucket’s Settings, edit the CORS Policy and use your own address:
ETag in ExposeHeaders is required: the upload is sent in parts, and the browser has to read each part’s ETag to finish it. Skip this step if you set UPLOAD_VIA_SERVER=true, where the browser never talks to R2.6
Recreate the container
Recreate the app container so it reads the new values. A plain The database, Redis and Temporal keep running. Avoid
docker restart or
docker compose restart keeps the old container and its old environment.In the folder that holds docker-compose.yaml, recreate only the postqueen service:docker compose down for a settings change: on
a host that runs other Compose services in the same project, it stops them too. On Kubernetes, see
Kubernetes with Helm.Upload an image in Media. It appears in the library, and the object is in the bucket.
Good to know
- Back up the bucket and the database together. The database stores only each file’s address. Restore them from the same moment: Backups and upgrades.
- Give backups their own token, with read access to this bucket and write access to the backup bucket, so a backup tool cannot delete the media it protects.
- Unfinished uploads clean themselves up. A multipart upload that never completes leaves parts that appear in no listing. Every R2 bucket has a default lifecycle rule that aborts them after seven days.
Related
- Uploads and storage: local disk against R2, and
UPLOAD_VIA_SERVER - Configuration reference
- Cloudflare’s R2 documentation