B2 storage
A Backblaze B2 bucket with credentials scoped to it, at the lowest price per byte.
AI/LLM: this page is available in plain markdown at /docs/resources/b2.md
npx extraorbital add b2✓ Provisioned b2/default (bucket eo-a1b2c3-cosmic-otter-default, us-west-004)
✓ Wrote 5 variables to .env
B2_BUCKET, B2_ENDPOINT, B2_REGION, B2_ACCESS_KEY_ID, B2_SECRET_ACCESS_KEYA project that mentions any B2_* variable gets this from
extraorbital provision without naming it.
B2 is S3-compatible, so every S3 client works against it unchanged. It is the
cheapest of the three per byte stored — about a third of r2
and a quarter of s3 — and it charges nothing for
operations. The trade is bandwidth: egress is free only up to three times what
you store.
What it writes
| Variable | Example |
|---|---|
B2_BUCKET | eo-a1b2c3-cosmic-otter-default |
B2_ENDPOINT | https://s3.us-west-004.backblazeb2.com |
B2_REGION | us-west-004 |
B2_ACCESS_KEY_ID | 0045f1a… |
B2_SECRET_ACCESS_KEY | K004… |
The key reaches that bucket and no other. Bucket names carry a prefix because Backblaze names are unique across all of Backblaze, not just within an account.
Options
npx extraorbital add b2 [--slug <name>] [--public]| Flag | Default | Description |
|---|---|---|
--slug | default | A second, fully separate bucket in the same project |
--public | off | Objects are publicly readable |
There is no --region: Backblaze puts a bucket where the account lives, and
B2_REGION reports where that turned out to be.
Options apply at creation. Re-running add with different options returns the
existing bucket unchanged — remove it first if you need a different one.
What it costs
Backblaze's published rates, passed on unchanged, counted against your plan's allowance:
| Rate | |
|---|---|
| Storage | $0.00695 / GB-month ($6.95 / TB) |
| Operations | free — uploads, downloads, listing and copies alike |
| Egress | free up to 3× what you store, then $0.01 / GB |
Two honest caveats.
Egress is not billed per bucket. Backblaze reports no per-bucket download
counter, so an overage cannot be attributed to the bucket that caused it. We
watch the account-level ceiling instead rather than invent a share — but if you
serve far more than you store, r2 has no egress charge at
all and is the better fit.
Storage is sampled, not replayed. Backblaze publishes no usage history, so a day is billed on a reading taken during it rather than reconstructed afterwards.
Usage appears in extraorbital ledger a day at a time, once each UTC day has
closed.
File retention on the Free plan
On the Free plan, files are hidden 30 days after they are uploaded and deleted 10 days after that. Backblaze does this itself, as a bucket lifecycle rule; nothing of ours sweeps your bucket.
Those ten days are the point. A hidden file is invisible to reads and lists but
still there, so a free account that starts paying within the window gets its files
back. After the window they are gone, and r2 has no window
at all — its expiry is an outright delete.
Paying lifts the rule within the hour. Lifting it stops further expiry; it does not un-delete anything already past the ten days.
You cannot change this yourself: the key you are given can read and write objects, not reconfigure the bucket, which is what makes the rule a rule.
This is separate from the 30-day idle rule that deletes an unused resource: a busy free bucket survives that one and still loses last month's files.
Use it
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const b2 = new S3Client({
region: process.env.B2_REGION,
endpoint: process.env.B2_ENDPOINT,
forcePathStyle: true,
credentials: {
accessKeyId: process.env.B2_ACCESS_KEY_ID!,
secretAccessKey: process.env.B2_SECRET_ACCESS_KEY!,
},
});
await b2.send(
new PutObjectCommand({ Bucket: process.env.B2_BUCKET, Key: "report.json", Body: data }),
);Next
- R2 storage — free egress, if you read more than you store
- Quickstart — slugs, idempotency and sharing
- Pricing — plans, allowances and rates