R2 storage
Add a Cloudflare R2 file bucket with free download bandwidth.
npx extraorbital add r2Free-plan files expire after 30 days. Deleted files cannot be restored.
- 01 / Run the commandGet the service
- 02 / Read your .envUse its credentials
- 03 / Connect your appCall the SDK or API
Limits
| Limit | Free · Prototype | Paid |
|---|---|---|
| Storage | Your allowance | Usage billed |
| Requests | Your allowance | Usage billed |
| Bandwidth | Free egress | Free egress |
| File retention | Deleted 30 days after upload | Kept until you delete it |
| Backups | None | Charged as storage |
| Inactive resource | 30 days after last use | Persistent |
Free allowances are shared across the account. Provider limits still apply on paid plans.
Use it
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const r2 = new S3Client({
region: "auto",
endpoint: process.env.R2_ENDPOINT,
// R2 addresses a bucket as a path under the account host, not as a
// subdomain of it. Without this an SDK on its defaults finds nothing.
forcePathStyle: true,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
});
await r2.send(
new PutObjectCommand({ Bucket: process.env.R2_BUCKET, Key: "report.json", Body: data }),
);Environment variables
The CLI writes these to .env or an existing .env.local.
| Variable | Purpose |
|---|---|
R2_BUCKET | Bucket name |
R2_ENDPOINT | Endpoint for the bucket’s jurisdiction |
R2_ACCOUNT_ID | Cloudflare account ID |
R2_ACCESS_KEY_ID | Bucket access key |
R2_SECRET_ACCESS_KEY | Private signing secret |
Specifics
Options
npx extraorbital add r2 [--slug <name>] [--public] [--region <jurisdiction>]| Flag | Default | Description |
|---|---|---|
--slug | default | A second, fully separate bucket in the same project |
--public | off | Objects are publicly readable |
--region | default | An R2 jurisdiction: default, eu or fedramp |
--region selects a storage jurisdiction. eu restricts storage to the European Union. Accepted values are default, eu, and fedramp; other values return an error.
R2_ENDPOINT matches the selected jurisdiction.
Options apply at creation. Reusing a slug does not change the bucket. Use a new slug for a different configuration.
Access
Credentials are limited to this bucket. --public makes objects readable without authentication. Use a new slug for a different configuration; repeating add reuses the existing bucket.
File retention
Free-plan files expire 30 days after upload, under a Cloudflare lifecycle rule.
Upgrading removes the rule within an hour. Deleted files cannot be restored. B2 offers a 10-day recovery window.
Expiry details:
- Expiry is approximate. Cloudflare typically deletes objects within 24 hours of the deadline.
- You cannot change this yourself. The supplied credentials manage objects, not bucket lifecycle rules.
File expiry applies even when a bucket is active. The separate inactivity policy deletes resources unused for 30 days.
Pricing
| Usage | Paid rate |
|---|---|
| Storage | $0.015 / GB-mo |
| Requests | $4.50 / M writes, $0.36 / M reads |
| Bandwidth | Free egress |
See pricing for team subscription fees, allowances, and billing terms. Plans and billing ↗
Deletes and aborted multipart uploads are free. Usage appears after each UTC day closes.