Storage: r2:// (Cloudflare)
The same disaggregated ObjectStorage backend as s3://, pointed at Cloudflare R2. R2 is S3-compatible, so everything works identically — with one structural advantage: zero egress fees, which directly lowers the cost of cache misses and cold starts. See the shared model in the overview.
Why R2 specifically
On a disaggregated engine, a cache miss is a read out of object storage — billed as egress on S3/GCS. So a cold cache costs more on those providers. R2 charges zero egress, which removes that cost structurally. That makes it a natural fit for:
- Read-heavy workloads with a large working set that doesn't fully fit in cache.
- Frequently cold-started / scale-to-zero databases (each cold start re-reads pages).
- Many branches read concurrently off one base.
The engine, durability model, and API are otherwise identical to the S3 backend.
Open it
import { open } from "@twilldb/bun";
using db = open("r2://my-bucket/mydb");
cargo run -p twill-server -- --listen 0.0.0.0:5433 --db r2://my-bucket/mydb
Credentials
R2 uses S3-style access keys (create an R2 API token in the Cloudflare dashboard), supplied via the standard variables. The R2 S3 endpoint is your account-specific URL; set it through your deployment configuration.
export AWS_ACCESS_KEY_ID=... # R2 access key id
export AWS_SECRET_ACCESS_KEY=... # R2 secret access key
# R2 S3 endpoint: https://<account-id>.r2.cloudflarestorage.com (set via deploy config)
Secrets stay in the environment
Keep R2 tokens in environment configuration, never in the connection string or the repo.
What you gain (same as S3, plus zero egress)
- Scale-to-zero — stateless compute idling to nothing; cold starts warm by WAL replay. See Scale-to-zero.
- Durable single-writer lease — CAS-epoch fencing against split brain.
- Copy-on-write branching — child key-prefix overlays, no page copies. See Branching.
- Zero egress — cache misses and cold reads don't incur per-GB transfer charges.
Note on deployment
R2 also pairs with the exploratory WASM / Cloudflare Workers deployment track (a parallel deliverable, not the native critical path). On the native server/embedded path documented here, R2 is simply selected by the r2:// scheme.