I'll be honest: when I was architecting Jottings, storage costs scared me more than they should have.
Building a serverless microblogging platform means users will upload photos, write long-form posts, and expect their content to be lightning-fast worldwide. That's terabytes of data flowing in and out. AWS S3 was the obvious choice—everyone uses it, the tooling is rock-solid, and the documentation is everywhere.
But then I did the math on egress fees.
The S3 Egress Fee Trap
Here's the thing about S3 that nobody wants to talk about until you actually need it: storage costs almost nothing, but moving that data out costs real money.
S3 pricing for the us-east-1 region breaks down roughly like this:
- Storage: $0.023 per GB/month (totally fine)
- Egress to internet: $0.09 per GB (ouch)
If a user uploads a 5MB photo and 100 people view it, that's 500MB of egress. Scale that to thousands of users and hundreds of thousands of views? We're talking thousands of dollars monthly.
You could argue "just use CloudFront"—AWS's CDN. And you'd be right, except:
- CloudFront adds another $0.085/GB of origin shield if you want to reduce S3 load
- You're now paying for two separate AWS services with separate billing
- S3 still charges egress to CloudFront (another $0.020/GB)
- Setting it up correctly requires understanding origin groups, cache behaviors, and TLS certificates
For an indie project like Jottings, this complexity and cascading costs felt wrong. There had to be a better way.
Enter Cloudflare R2
I discovered Cloudflare R2 almost by accident. I was already using Cloudflare for DNS and the edge network. Turns out they have their own object storage—and it works completely differently.
R2's pricing is refreshingly simple:
- Storage: $0.015 per GB/month (slightly cheaper than S3)
- Egress: $0.00 (yes, really—the "R" stands for "zero egress")
That's not a typo. Cloudflare charges nothing for egress when you serve from R2 through their global CDN. This is the fundamental difference that changes everything.
But wait—there's more.
Why We Actually Chose R2
The zero egress fee is the headline, but the real magic is in how it fits together with Cloudflare's existing infrastructure.
Native CDN Integration: When you upload to R2 and serve through your-domain.cloudflare.workers.dev or a custom domain, the content is automatically cached across Cloudflare's global network. No separate CDN setup. No configuration. It just works.
For Jottings, we serve media from static.jottings.me with a simple Worker that routes requests to R2. The content is cached at 200+ edge locations worldwide. A user in Tokyo and a user in São Paulo see the same photo in milliseconds, not because of some complex CDN tuning, but because Cloudflare's edge is everywhere.
S3-Compatible API: R2 isn't some weird proprietary system. You can use the exact same SDK you'd use for S3 (aws-sdk-js). Our migration took hours, not weeks. The API calls look identical:
const s3 = new S3Client({
region: "auto",
endpoint: "https://[account-id].r2.cloudflarestorage.com",
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY,
secretAccessKey: process.env.R2_SECRET_KEY,
},
});
Simpler Operations: With S3, we'd need to manage CloudFront, origin shields, cache invalidations, and multiple billing records. With R2, it's one service. Upload to R2, route with Workers, cache at the edge. Done.
Cost Predictability: There's no "surprise egress bill" when a post goes viral. Every user could view every photo a thousand times, and the bill stays the same. This matters more than people realize—it lets us grow without financial terror.
The Real-World Impact for Jottings
In production, here's what this means:
- Media uploads go directly to R2 from the browser (presigned URLs)
- Site assets (CSS, JavaScript) are stored in R2 and served globally
- Generated HTML from static site builds gets stored and served at the edge
- User-generated sites (the custom domains feature) are entirely R2 + Workers
Our storage costs are predictable. Our performance is consistent worldwide. Our infrastructure is simple enough that I can explain it to other developers in under five minutes.
If someone's blog post hits the front page of Hacker News and 50,000 people view their photos in an hour? The system scales instantly, the CDN handles it, and our bill doesn't change. That's the peace of mind that matters for a bootstrapped product.
Should You Use R2?
This depends entirely on your use case.
Use R2 if:
- You're building an indie app and every dollar counts
- You need global distribution and don't want CDN complexity
- You want predictable, simple billing
- You're comfortable with the Cloudflare ecosystem
- You're using Workers, Pages, or other Cloudflare products
Use S3 if:
- You have enterprise contracts that make costs irrelevant
- You need specific AWS features (Glacier, Snowball, cross-region replication)
- You're already deep in the AWS ecosystem
- You need object retention or regulatory compliance features R2 doesn't support yet
For us, R2 was the clear choice. It lets Jottings stay lean, predictable, and global without the cognitive overhead of managing a complex multi-service setup.
The storage wars aren't about raw features anymore—they're about whether you can understand your bills, scale without surprises, and focus on building your actual product instead of infrastructure.
We chose R2, and we've never looked back.
If you're building something and wondering about your own infrastructure, I'd love to hear about it. Building in public is a lonely process—knowing others are figuring out these same questions makes it feel less so.
Create your Jottings microblog today, completely free.