Why I'm hosting personal projects on Cloudflare Workers in 2026
Workers, D1, R2, AI Search and Zero Trust: a tour of the Cloudflare stack behind this site and its AI chat, and why the bill comes to about the price of a coffee.
Info
Updated October 2026: This article has been updated following several changes to Cloudflare’s pricing, and feature offering.
Last year I rebuilt this site from the ground up on Next.js, with Sanity for the writing section, a growing list of browser-based developer tools, and a contact form. When it came to deciding where it would all be hosted, there wasn't much of a decision to make. It went onto Cloudflare Workers, which is where everything I've built for myself over the past couple of years has ended up.
Fair warning: I'm going to talk about money a bit in this post. The money is most of the point.
For context, a good chunk of my career has been spent designing systems on Azure, so this isn't a case of not knowing what the big clouds offer. Actually, it's closer to the opposite. Azure, AWS and GCP have fantastic offerings for small-scale apps, and Azure Container Apps are great for hosting .NET API-first back-ends cheaply. When it comes to hosting JavaScript apps that don't get loads of traffic, however, Cloudflare really shines.
The hosting
This site is a Next.js app compiled for Workers by OpenNext. The build takes standard Next output and produces a single Worker that serves the pages, API routes and static assets from Cloudflare's edge.
The free plan gives you 100,000 requests a day, with up to 10 milliseconds of CPU time per invocation. That CPU figure sounds absurd until you learn it measures actual compute rather than wall-clock time, so the hundreds of milliseconds a request spends waiting on a CMS or a database don't count. A server-rendered page on this site fits inside that budget with room to spare. The paid plan is $5 a month with 10 million requests and 30 million CPU milliseconds included, and that's the plan this site runs on.
The reason was bundle size. Free-plan Workers used to be capped at 3 MiB compressed, and I met that limit the hard way when Sanity Studio tried to bring its 15 MiB of JavaScript along for the ride. Studio now lives on Sanity's own hosting rather than inside this app, which, in hindsight, is where it should have been all along. Even without it, the Next.js runtime, the Sanity client and the Shiki syntax highlighter came to about 3.7 MiB compressed, so I paid the $5.
That particular wall has since gone. In September 2026 Cloudflare dropped the compressed limit entirely, and every plan now gets 64 MiB uncompressed. This site would fit on the free plan today, but it's stayed on Paid, because $5 a month buys a lot of headroom on something I lean on this much.
The storage
Two pieces here: a useful free tier, then prices that stay low for most personal projects.
D1 is Cloudflare's SQLite database. Provisioning it is a single wrangler command and a binding in your config. The free tier covers 5 million row reads and 100,000 row writes a day, with 5 GB of storage, and Time Travel gives you point-in-time restore on top: 7 days on the free plan, 30 on Paid. On the paid plan the included numbers jump to 25 billion reads and 50 million writes a month. I generally use Supabase for Postgres and Auth, and plan to discuss Supabase vs Neon vs others in a separate post.
This site's own D1 database is a small one. OpenNext keeps its revalidation tags there, so when I publish a post in Sanity, a webhook expires the right tags and every page that shows that post goes stale at once, wherever it's cached.
R2 is the object store. It's an S3-compatible API, 10 GB free, then $0.015 per GB-month, and there are no egress fees. I mostly use R2 for caching, personally and professionally. OpenNext can use an R2 bucket as the incremental cache for a Next.js deployment, so rendered pages persist across the fleet instead of being rebuilt per isolate.
The front door
The contact form on this site is protected by Turnstile, Cloudflare's CAPTCHA replacement. It's free for up to 20 widgets, and in managed mode most humans never see a challenge (no traffic-light photographs 🙂).
Because my DNS runs through Cloudflare too, the whole site sits behind their proxy. DDoS protection is on by default, and the free plan includes enough WAF to write custom rules against the obvious nuisance traffic (there is always nuisance traffic; check your logs sometime). Cloudflare is the firewall for everything public I run at the moment, personally and for most of my professional work. The hardware in my office at home is a slightly different story.
The homelab
I run a home server: Immich for photos, a NAS for backups, and countless other apps. The traditional way to reach these from outside the house is port forwarding and a quiet prayer that you configured it properly.
Cloudflare Tunnel is Cloudflare's way around that. A small daemon called cloudflared runs on the server and makes an outbound connection to Cloudflare's network, and your services become hostnames on your own domain with zero inbound ports open on the router. From the internet's side there is nothing to scan, and tunnels are free.
My photo library doesn't go through one, though. Immich is only reachable over Tailscale, a WireGuard-based VPN that puts my devices and the home server on one private network wherever I happen to be. Nothing about Immich is on the public internet, which suits a library of family photos. It also means I don't have to think about Cloudflare's CDN terms, which reserve the right to limit accounts serving video or a disproportionate share of large files through the proxy. If you haven't used Tailscale, I highly recommend it.
Cloudflare Access is where Cloudflare earns its place on the private side. Access puts an SSO wall in front of any hostname: I authenticate once with my Google account and whatever sits behind it opens as if it were on the LAN. The Zero Trust free plan covers 50 users, and professionally I use it to gate UAT and staging environments for small projects I build for other people, along with internal projects.
Putting an Access policy in front of a staging hostname takes about ten minutes and costs nothing, and anyone who isn't on the list gets a login page they can't get past.
The AI bits
The newest thing on this site is AI Me, a chat that answers architecture questions as me, grounded in what I've written here. It's built almost entirely from Cloudflare parts. When I publish something in Sanity, a webhook calls the site, which turns the document into Markdown and writes it to a private R2 bucket. AI Search indexes that bucket and re-syncs every hour, so a new post becomes something AI Me can answer from without a rebuild or a deploy.
When a question comes in, the Worker pulls the closest matching chunks out of AI Search and hands them to Claude Sonnet 5.5. The model call goes through the Workers AI binding and AI Gateway with unified billing, so Cloudflare charges Anthropic's token prices against prepaid credits and there's no Anthropic API key anywhere in the project. In front of all that sits a rate-limit binding, a few lines of Wrangler config that cap how often a single IP address can ask, plus Turnstile again on the first message of each conversation.
The bill
Totting it all up for this site:
- Workers hosting: $5 a month on Workers Paid. Plenty of personal projects would fit inside the free plan.
- D1 database: included, with 30 days of point-in-time restore on Paid.
- R2 storage and caching: free to a few pennies. No egress fees, ever.
- Turnstile: free.
- AI Me: pay as you go, out of prepaid AI Gateway credits.
- Tunnel and Access: free on the Zero Trust free plan, up to 50 users.
- DNS, DDoS protection, basic WAF: free with the domain on Cloudflare.
The equivalent AWS setup (a small EC2 instance or two, RDS, a load balancer, a NAT gateway) can land somewhere between £40 and £100 a month.
Professionally, we've chosen to migrate all internal JavaScript-based apps and small public-facing apps out of Azure and into Cloudflare. The observability and analytics tools on Workers are somewhere between sufficient and good, the Security Analytics on the firewall are easy to find your way around, and overall the bill is hugely reduced.
One change to keep an eye on: from 1 December 2026, Workers Logs move onto Cloudflare's new Observability pricing. Paid plans include 50 GB of log ingestion a month and charge per GB beyond that. A personal site won't come close, but a busy professional estate with verbose logging might.
The catch
Of course, this is a lot of eggs in one basket. Cloudflare is my registrar, DNS, CDN, compute, database, object store and auth layer. When they have a bad day (and they've had a few, like the 18 November 2025 outage that took a fair slice of the internet down with it), I don't have a failover plan, so I wait for them to fix it like everyone else.
The lock-in isn't bad, mind you. D1 is SQLite underneath, so an export is just a file, and R2 speaks the S3 API, which makes leaving it a copy job. AI Search is the newest dependency, though what it indexes is plain Markdown sitting in a bucket. The stickiest part is the Worker runtime itself, with its bindings and its not-quite-Node environment, though OpenNext keeps the application layer portable enough that the rework would be measured in days rather than months. It didn't take long to move some apps out of Docker on Azure Container Apps and into Cloudflare, and it wouldn't take long to move them back.
For my personal projects, the whole estate (this site, the tools on it and AI Me) comes to $5 a month plus whatever AI Me's visitors use in Gateway credits, which is roughly the price of a coffee. If that ever changes you'll get a follow-up post, but my money is on the coffee inflating first.
Further reading
- Cloudflare Workers documentation
- Workers pricing
- Workers limits
- OpenNext for Cloudflare
- Cloudflare D1 documentation
- Cloudflare R2 pricing
- Cloudflare Turnstile documentation
- Announcement: Free Tunnels for Everyone
- Cloudflare Zero Trust plans
- AI Search documentation
- AI Gateway documentation
- AI Gateway unified billing
- Tailscale: how it works
- Cloudflare outage on November 18, 2025
Related posts
Taking control: self-hosting my photo library with Immich
Why I moved my photo library off Google Photos and onto Immich on my own NAS, and how the setup, the folder albums and the Google Takeout import went.
My home NAS setup: a dive into the unknown
Building a silent TrueNAS server for family photos, films and backups, and how it has changed since: Jellyfin on the NAS only, Storj's new pricing and an honest look at RAIDZ1.