When to Use a VPS vs Cloudflare Workers in 2026
TL;DR
Serverless (Cloudflare Workers) gives you $0 global hosting and push-and-forget deployment. Traditional servers (Docker/VPS) give you full control and fixed pricing. Here's when to pick which, backed by real projects and real bills.
Key Takeaways
- Cloudflare Workers free tier is generous (100K req/day, D1 5M reads/day) — most side projects never pay a cent
- D1 is SQLite, not Postgres. Works for simple CRUD. Falls apart with complex queries or heavy writes.
- Serverless bills can spike without warning (D1 $5K, Workers $2.5K). Traditional VPS bills are predictable.
- The best stack for 2026: Cloudflare Workers (compute) + Supabase (database). Serverless speed with real Postgres.
- Use Docker/VPS when your app runs 24/7 or needs unlimited CPU — Telegram bots, video processing, cron-heavy services.
I’ve shipped on both sides of this divide. Docker Compose on a VPS. Cloudflare Workers on the edge. Heroku when $7/mo felt reasonable. Fly.io when the free tier was still generous. Thirty-plus projects later, I have strong opinions about when each makes sense — and when it doesn’t.
Here’s the quick tour of what I’m comparing:
| Project | Architecture | Infrastructure | Cost/Month | Users |
|---|---|---|---|---|
| Alita Robot | Traditional | Docker Compose (Dokploy) + Postgres + Redis on Oracle free VPS | $0 (was $7 on Heroku) | 300K+ |
| Logwell | Traditional | Docker (Fly.io) + Postgres | $0 (free tier) | Self-hosted log pipeline |
| Clickfolio | Serverless | Cloudflare Workers + D1 + R2 + Queues + Durable Objects | $0 (free tier) | 500+ MAU |
| PickMyClass | Hybrid | Cloudflare Workers + Supabase | $0 (free tier) | Thousands ASU students |
Four projects. Three architectures. One conclusion: there’s no universal answer — but there absolutely is a right answer for your specific project.
What is Cloudflare Workers? It’s a serverless platform that runs JavaScript, TypeScript, or WebAssembly at the edge — 330+ cities worldwide — with zero cold starts and no infrastructure to manage. You write code, run
wrangler deploy, and it runs everywhere.
How It Started: Heroku, Python, and Why I Left
Alita Robot started in 2021 as a fork of a Python Telegram bot called Mary. Someone had built Mary to manage Telegram groups, and a friend wanted a custom version. I forked it, renamed it, and threw it on Heroku’s $7/mo Hobby dyno.
For a while, it was fine. But Telegram bots have this problem: they never stop. Users message at 3 AM. Webhook updates arrive constantly. Heroku’s free dynos sleep after 30 minutes of inactivity — so the paid dyno was mandatory. $7/mo for what was essentially a hobby project.
The real breaking point was concurrency. Python’s single-threaded model and Heroku’s limited worker processes meant when 50 groups sent updates simultaneously, the bot lagged. I decided to rewrite the entire thing in Go.
Go’s goroutines handled concurrent Telegram webhooks cleanly. Channels replaced the janky message queue I’d hacked together in Python. The rewrite took a month of weekends, but the result was a bot that could handle 300K+ users without breaking a sweat.
The question was: where do I put it?
The Docker Era: Self-Hosting on Oracle’s Free VPS
Oracle Cloud’s Always Free tier is absurdly generous: 4 ARM cores (Ampere Altra), 24GB RAM, 200GB block storage. No credit card tricks. No 12-month limit. Just free.
I learned about it from a Reddit thread and immediately migrated Alita off Heroku. The stack:
docker-compose.yml:
- Go binary (compiled, statically linked)
- Postgres 18 Alpine
- Redis 7 Alpine
- Dokploy for deployment UI
Oracle’s free tier gave me full control — real Postgres with extensions, custom configs, and direct psql access. The Go binary had unlimited CPU time to process webhooks as fast as the CPU allowed. No cold starts, fixed billing with a hard cap, and 24/7 uptime for a bot that never sleeps.
But I’ve woken up to “bot not responding” messages more times than I can count because Docker decided to restart Postgres at 3 AM. Debugging meant ssh-ing in and running docker compose logs while half asleep. The free tier ties you to one datacenter, so users in Southeast Asia got noticeable latency. And DDoS protection was entirely my problem — a traffic spike didn’t just slow things down, it crashed the server. Maintenance never stopped: Postgres backups, Redis memory policies, disk space monitoring, OS updates. That has never happened on Workers.
The Serverless Pivot: Cloudflare Workers
Around the same time I was maintaining Alita on a VPS, I started building Clickfolio — a portfolio builder with AI features. I picked Cloudflare Workers for the entire backend.
The stack:
Cloudflare Workers (Hono framework) → D1 (SQLite) + R2 (storage)
→ Queues (async jobs) + Crons (scheduled tasks)
→ Durable Objects (WebSocket coordination)
→ Workers AI (Gemini Flash Lite, free tier)
PickMyClass, another project serving thousands of ASU students, uses a similar pattern but swaps D1 for Supabase.
What works:
- $0 hosting. Everything — compute, database, storage, queues, crons — runs on free tiers
- Global edge. 330+ cities. Your code runs within 50ms of any user on Earth
- Push-and-forget deployment.
wrangler deployhandles SSL, CDN, and scaling. No Dockerfiles, no reverse proxies, no maintenance windows - Durable Objects + WebSocket Hibernation. Real-time features at near-zero cost because DOs hibernate when idle
- Queues + Crons handle async work without Redis or external cron jobs. At-least-once delivery with automatic retries
What hurts:
- D1 is SQLite, not Postgres. JSON is stored as TEXT (no JSON operators). Booleans are integers (0/1). No row-level security. No extensions. If you’re used to Postgres, D1 feels like a toy
- CPU limits bite hard. Free tier: 10ms per request. Paid: 30 seconds (extendable to 5 minutes). Write inefficient code and your requests return 1102 errors
- No filesystem at runtime. Everything must be prebuilt or fetched from R2. No
fs.readFileSync, no temp directories - Debugging is fragmented.
wrangler devworks locally, but production V8 isolates behave differently. Tracing a request across Workers → Queues → DO is three separate logs - Write-heavy apps on D1 are dangerous. A missing WHERE clause in an UPDATE can run a $5,000 bill in under 10 seconds. D1 has no per-query cost limits and no real-time usage alerts
Cost Comparison: Real Numbers
Here’s what I actually pay, and what you’d pay at each tier:
| Service | Free Tier | Paid Entry | Key Limits (Free) | Overage Cost |
|---|---|---|---|---|
| Cloudflare Workers | 100K req/day | $5/mo | 10ms CPU, 128MB mem | $0.30/M requests |
| Cloudflare D1 | 5M rows read/day | Included in $5 | 100K writes/day | $1.00/M writes |
| Cloudflare R2 | 10GB storage | $0.015/GB-mo | Class A ops free, B $0.36/M | $0.36/M ops |
| Oracle Cloud VPS | 4 OCPU, 24GB RAM | Always free | 200GB block, 10TB egress | None (hard cap) |
| Hetzner VPS | N/A | ~$5/mo | 2 vCPU, 4GB RAM | Fixed price |
| Supabase | 500MB DB, 50K MAU | $25/mo | 2GB file storage | $0.00326/GB egress |
| PlanetScale | No free plan | $5/mo | Single node, 512MB | Row-based |
Real stories behind these numbers:
- Alita on Heroku cost $7/mo. Migrating to Oracle’s free VPS brought it to $0. I pay nothing for a bot serving 300K+ users
- Clickfolio costs $0 total — Workers free tier, D1 free tier, R2 free tier, Workers AI free tier. 500+ monthly active users and my bill is a flat zero
- PickMyClass costs $0 — Workers free tier plus Supabase’s absurdly generous free tier. Thousands of ASU students, no bill
- Logwell runs on Fly.io’s free tier. $0 for a log aggregation pipeline
The horror stories (verified, not sensationalized):
- In July 2025, a missing WHERE clause on D1 caused a $5,000 bill in under 10 seconds (ofsecman.io postmortem)
- In January 2026, a static site on Netlify was hit with a DDoS attack — $104,000 bill (dev.to)
- In June 2025, Workers for Platforms zombie scripts racked up $2,500 in surprise charges (Cloudflare Discord)
- In 2024, a Firebase project was DDoS’d to a $98,000 bill
- In May 2024, Cloudflare Enterprise upsell demanded $120,000/year with a 24-hour ultimatum (robindev.substack.com)
The pattern across all these stories: serverless bills can spike without warning. Traditional VPS bills are fixed. You trade pricing predictability for operational convenience.
Database Trade-offs: D1 vs Supabase vs PlanetScale vs Self-Hosted
The database decision is where most projects diverge between architectures. Here’s the honest breakdown:
Self-hosted Postgres (Alita, Logwell): Full control over your database. Real Postgres with extensions, custom configs, and direct access. No usage-based billing — you pay for the VPS and that’s it. The cost is your time: setting up backups, managing replication, handling upgrades, monitoring disk space. Fine if you enjoy ops work. Annoying if you just want to ship features.
D1 (Clickfolio): SQLite distributed across Cloudflare’s edge. Global read replicas mean your queries are fast everywhere. Free tier covers 5M row reads and 100K writes per day — enough for most side projects. But D1 is SQLite, not Postgres. No JSON type. No row-level security. Booleans stored as integers. Complex JOINs get slow. And the billing is terrifying: D1 has no query cost limits, no row-update thresholds, and no real-time usage alerts. A bad query can cost thousands.
Supabase (PickMyClass): Managed Postgres with Auth, Realtime, and Storage bundled in. The free tier gives you 500MB database and 50K monthly active users — generous enough for most projects. If you outgrow it, the $25/mo Pro plan is still cheaper than running your own VPS with equivalent reliability. This is what I use for PickMyClass and recommend to anyone who wants real Postgres without ops work.
PlanetScale: $5/mo for a single node with real Postgres. Row-based pricing with built-in cost controls — unlike D1, you can set spending limits and get alerts before things spiral. The free tier is gone now, but $5/mo for managed Postgres with cost controls is still a good deal. Good middle ground between Supabase’s free tier and self-hosting.
My recommendation: Supabase for side projects (free tier is too good to ignore). PlanetScale if you need cost controls at scale. D1 only if you’re all-in on Workers and your write volume is low. Self-host if you have time to maintain it and need full control.
The Middle Ground: Workers + Managed Database
Most of my projects follow the same pattern now: Workers for compute, Supabase for data.
PickMyClass is the clearest example. Workers handle API requests, background jobs (Queues + Crons), and serve the frontend. Supabase handles authentication, the Postgres database, and real-time updates when class availability changes. Total cost: $0.
This hybrid approach gives you the best of both worlds:
- Serverless compute benefits: global edge, $0 hosting, automatic scaling, push-and-forget deployment
- Real Postgres benefits: transactions, JSON operators, row-level security, extensions, proper tooling
Supabase’s free tier is generous (500MB database, 50K MAU), and if you outgrow it, the $25/mo Pro tier is reasonable. PlanetScale at $5/mo is the next step up if you need cost controls and don’t want to manage infrastructure.
This is my default stack recommendation for 2026: Cloudflare Workers + Supabase. Fall back to self-hosted Docker only when the app needs to run continuously or has heavy database requirements that outgrow managed tiers.
Cloudflare Workers vs Vercel vs Netlify: Bandwidth and Billing Reality
People ask me this constantly. The short answer: Workers wins for API-heavy apps because it has no bandwidth caps.
Vercel and Netlify cap bandwidth. On their free tiers, you’ll hit paywalls faster for API-heavy apps than you will on Workers. Workers charges $0.50 per million requests for overage — predictable and cheap for most side projects. Vercel’s hobby tier starts throttling after 100GB bandwidth. Netlify’s free tier caps at 100GB too.
The difference matters when your project grows. A portfolio builder that serves AI-generated assets (like Clickfolio) can blow through 100GB of bandwidth quickly. On Workers, that’s still $0. On Vercel or Netlify, you’re forced onto a paid plan immediately.
The counter-argument: Vercel and Netlify have better DX for frontend frameworks. Their deployment previews, automatic branch deployments, and Next.js optimization are genuinely great. If you’re building a pure frontend site with minimal API calls, either platform is fine. But if your app talks to a database, processes files, or handles real-time features, Workers’ pricing model is built for that scale.
When to Pick What: A Decision Framework
I don’t actually use a checklist when I pick a stack. I ask one question: does this thing need to stay awake? Telegram bots, cron jobs, anything with a WebSocket — they can’t sleep, so they go on a VPS. Everything else defaults to Workers + Supabase.
The only time I reach for D1 is when I’m too lazy to spin up Supabase and I know the app will barely touch the database. That’s the entire decision tree. Thirty projects and it really is that simple.
Workers alone works when your write volume is low, you don’t need real Postgres features, and you want push-and-forget deployment with zero maintenance. The free tier handles 100K requests per day and 5M D1 row reads — generous enough that most side projects never pay.
Workers + Supabase is what I use for anything that needs real Postgres: JSON operators, row-level security, extensions, proper tooling. Supabase’s free tier covers 500MB and 50K monthly active users. If you outgrow it, the $25/mo Pro plan is still cheaper than running your own VPS with equivalent reliability.
Docker on a VPS is for continuous uptime, unlimited CPU, and full database control. Oracle’s free tier gives you 4 ARM cores and 24GB RAM for $0. Hetzner starts at ~$5/mo. The trade-off is you’re on call forever — container restarts, disk filling up, DDoS protection, OS updates. They never stop.
My default for 2026: Workers + Supabase. Docker only comes out for the bots and services that never sleep.
The Things Nobody Warns You About
Both sides have sharp edges. Here’s what I learned the hard way:
Serverless surprises:
- D1 bills can bankrupt you in seconds. A missing WHERE clause, a loop that wasn’t supposed to loop — and you’re looking at thousands of dollars. D1 has no query cost limits and no real-time alerts. Add rate limiting and WHERE clause validation before you deploy
- Workers debugging is harder than
docker compose logs. Production V8 isolates run differently than local. Tracing a request across Workers, Queues, and Durable Objects means checking three separate dashboards - “At least once” delivery on Queues means messages can duplicate or go missing. You need orphan recovery crons for production apps
- Cloudflare’s free tier is permanent and generous, but the upsell to Enterprise can be aggressive. One user was told to pay $120K/year or get kicked off within 24 hours
Traditional server surprises:
- Your server will crash at 3 AM and nobody will fix it but you. Docker container restarts, Postgres connection pools exhausting, disk filling up — all happen while you’re asleep
- DDoS protection is your problem. Cloudflare absorbs 200+ Tbps attacks. Your VPS absorbs maybe 1 Gbps before it keels over
- Single-region latency is real. Users in India don’t care that your server is in Ashburn, Virginia — they care that it takes 300ms to respond
- You’re on call forever. OS updates, dependency patches, SSL certificate renewals, Postgres version upgrades — they never stop
The trade-off isn’t about features. It’s about which class of problems you’d rather deal with at 3 AM.
The Bottom Line
I’ve run projects at both extremes. Alita serves 300K+ users from a single Oracle VPS running Docker Compose. Clickfolio serves 500+ users from Cloudflare’s free tier across 330 cities. Both cost $0. Both work.
The real question isn’t “which is better” — it’s which set of problems you want to deal with at 3 AM.
I default to Workers + Supabase now. Docker only comes out for the things that can’t sleep. Both have their place. Knowing which is which is what the 30+ projects taught me.
Related posts:
- My 2025 Stack for Shipping 30+ Side Projects While Getting My Master’s — the stack decisions behind PickMyClass, Alita, and 28 other projects
- How I Host My Portfolio for $0/Month on Cloudflare — how divkix.me runs on Cloudflare Pages with full SEO at $0
- How I Built Clickfolio.me — A Full-Stack Portfolio Builder on Cloudflare Workers — deep dive into Workers, D1, R2, Queues, and Durable Objects for a production app
Frequently Asked Questions
Can I run a Telegram bot on Cloudflare Workers?
No — Workers WebSocket connections timeout and Workers don't run continuously. Use a VPS (Oracle free tier works great for this).
What happens when I exceed Cloudflare Workers free tier?
Requests return Error 1027. Upgrade to $5/mo Workers Paid for 10M requests/month. D1 overage is $1.00/M writes — add rate limits before you ship.
Is D1 a replacement for Postgres?
No. It's SQLite at the edge. Works for simple CRUD apps. Falls apart with complex JOINs, JSON queries, or high write volumes. Use Supabase or PlanetScale if you need real Postgres.
What's the cheapest way to host a side project in 2026?
Cloudflare Workers + D1 or Supabase free tier = $0. Add PlanetScale $5/mo if you need real Postgres with cost controls.
How do I prevent surprise cloud bills?
Rate limits in Workers, spending alerts in Cloudflare dashboard, WAF rules for DDoS protection, avoid D1 for write-heavy apps, prefer VPS for fixed-cost billing.
Cloudflare Workers vs Vercel vs Netlify?
Workers has no bandwidth caps and $0.50/M request overage. Vercel and Netlify cap bandwidth — you'll hit paywalls faster on those platforms for API-heavy apps.
What's Oracle Cloud's free VPS?
4 ARM cores (Ampere), 24GB RAM, 200GB block storage. Always free tier — no credit card tricks. Can run Docker Compose. Great for 24/7 apps like Telegram bots.
Sources & References
Related Posts
I Spent $600 Testing AI Coding Tools: Claude Code vs Cursor vs Copilot (2026 Results)
I've spent $50+/month on AI coding tools for a year. Here's what actually works, what's overhyped, and when to use each tool (including free local LLMs).
AI Models Compared 2026: I Tested All Ten Flagships So You Don't Have To
Ten flagship AI models. Five months of daily use. One spreadsheet of benchmarks. Here's the honest breakdown — and the winner wasn't what I expected.
I Built a Full-Stack AI App on Cloudflare Workers With D1, Durable Objects, and Queues — Here's What Actually Worked
Upload a PDF resume, get a live web portfolio. I built clickfolio.me entirely on Cloudflare's edge stack, D1 for data, R2 for files, Queues for async processing, Durable Objects for real-time WebSockets, and Gemini for AI parsing. Here's every technical decision, including the ones I regret.
Divanshu Chauhan (@divkix)
Software Engineer & MS CS @ Arizona State University. Currently SWE Intern @ Cloudflare. Based in Tempe, Arizona, USA.
Expertise: Cloudflare Workers, Serverless, VPS, Docker. More about divkix