Documentation
Storage (Cloudflare R2)
Cloudflare R2 file storage — bucket setup, credentials, presigned uploads, and S3-compatible API.
Motoko Base uses Cloudflare R2 for file storage. R2 is S3-compatible — the app uses the AWS SDK with a custom endpoint.
Official docs: Cloudflare R2 documentation
Setup
- Create an R2 bucket in Cloudflare dashboard → R2
- Create an R2 API token (Object Read & Write)
- Set env vars:
| Variable | Purpose |
|---|---|
R2_ACCOUNT_ID | Cloudflare account ID |
R2_ACCESS_KEY_ID | API token access key |
R2_SECRET_ACCESS_KEY | API token secret |
R2_BUCKET_NAME | Bucket name (optional /prefix suffix) |
R2_PREFIX | Optional key prefix override |
R2_ENDPOINT | Optional custom endpoint |
Without R2 env vars the app boots — the Storage demo shows a configuration message.
How uploads work
Client → Server Action (presigned PUT URL)
↓
Browser uploads directly to R2
↓
Server validates (HEAD) → marks file ready in PostgresFiles are never proxied through Next.js. The server issues a presigned URL; the browser uploads to R2. Metadata (name, size, owner) lives in the files table.
Upload limits and allowed MIME types: src/features/storage/config.ts.
S3-compatible API
R2 client: src/lib/r2.ts — uses @aws-sdk/client-s3 with:
- Endpoint:
https://{R2_ACCOUNT_ID}.r2.cloudflarestorage.com - Region:
auto - Path-style URLs (
forcePathStyle: true)
Presigned PUT (upload) and GET (download) URLs expire after 10–15 minutes.
Production
- Use a private bucket — access only via presigned URLs
- Set credentials as server-only env vars (never
NEXT_PUBLIC_*) - Avatars (Settings) also use R2 when configured — same credentials
- Consider lifecycle rules in Cloudflare for old objects — not shipped in the starter
For CORS: if browser uploads fail, add your app origin to the bucket CORS policy in Cloudflare. See R2 CORS docs.
Where to change this
| What | Where |
|---|---|
| R2 client & presigned URLs | src/lib/r2.ts |
| Upload actions & validation | src/features/storage/actions.ts |
| File metadata queries | src/features/storage/queries.ts |
| Limits & MIME allowlist | src/features/storage/config.ts |
| Storage UI | src/features/storage/components/dashboard/storage-page.tsx |
Import @/lib/r2 from server code only — never from client components.
Environment Variables — all R2 env vars.