- scale-to-zero tradeoffs
- runaway request patterns
- bill caps
The pricing dimensions that matter
As of 2026, the Workers Paid plan ($5/mo base) includes:- 10M requests/month, then $0.30 per million
- 25B D1 reads/month, then $0.001 per million
- 50M D1 writes/month, then $1.00 per million
- 400k GB-s compute, then $0.02 per million
- 1M Durable Object requests, then $0.15 per million
- Idle worker (no requests)
- Cold starts
- Reading env vars / bindings
- Every incoming HTTP request
- Every D1 query (reads cheap, writes expensive)
- Every DO invocation
- WASM execution time (GB-seconds)
Patterns that exceed a free tier
Polling loops without backoff
A client that polls/api/sync/pull every 500ms sends 172,800 requests/day. With 100 such clients, that is 17M requests/day, or about $5/day.
Fix: use WebSocket push (Durable Objects on Workers), or widen the polling interval with jitter. Pylon’s sync protocol supports cursor-based pulls, so clients pay only for deltas.
Loops inside a worker handler
A scheduled cron Worker triggers a function that writes to D1 in a loop. It gets rate-limited, retries the whole batch, and exceeds the D1 write quota. Fix:- paginate writes
- circuit-break on retry count
- use
ctx.waitUntilfor fire-and-forget work - measure before you ship
Durable Object hot-spotting
One room with 10k concurrent WS clients, all pinned to one DO, generates 1M+ DO requests/hour. Fix: shard rooms. On Workers, each Pylon room maps to one (sticky) Durable Object. Split a hot “global chat” room into many smaller rooms (for example, per region) to spread load across DOs instead of pinning everyone to one.Crawlers / bots
A/api/entities/Todo route that is not gated returns 404 to bots endlessly, but each hit still costs a Worker request.
Fix: use Cloudflare’s bot-fight mode and WAF rules for unauthenticated traffic on admin paths. Pylon’s router returns 401 or 403 quickly for unauthenticated non-public routes, but Cloudflare can block at the edge before the Worker even runs.
Errors in tight loops
A TypeScript function throws. The client retries immediately without backoff. Every retry costs a Worker and D1 round-trip. Fix:- client-side exponential backoff
- a server-side circuit breaker for degraded paths
- alerts on the 5xx rate rather than the request rate
Setting a budget cap
Cloudflare supports per-worker budget alerts, but not automatic cutoff. You have to write the cutoff yourself. Two patterns:Soft cap (recommended): alert and throttle
Hard cap: kill switch
Bind a KV namespaceBUDGET with a single key enabled: "1". At the top of your fetch handler:
Monitoring
Cloudflare’s analytics dashboard shows:- Request rate
- Error rate (4xx / 5xx)
- Subrequest count (every D1 or DO call counts)
- Wall-clock time
/api/sync/pullrate: anything above 10 req/sec/client is suspicious/api/entities/*error rate: a 403 spike signals a policy regression, and a 5xx spike signals a bug- WS connection count versus rejection rate (IP cap): rejections signal an attack
- D1 write volume versus
change_logappend rate: these should match
When a server is a better fit
Workers scale-to-zero does not help if:- You have steady traffic of 100 or more req/sec: a $25/mo AWS deploy will be cheaper
- You need shards or long-lived game simulations: Durable Object hibernation costs add up fast
- Your p99 latency matters and cold starts are not acceptable: a warm VPS is more predictable
- You need Postgres (
postgres-livefeature): Workers supports D1 only - You need large file uploads: Workers has a 100 MB request cap
Checklist
- Enable Cloudflare budget alerts the day you deploy.
- Add a kill switch KV namespace before problems start. It is easier to remove than to add under pressure.
- Client-side: always use backoff, jitter, and cursor pagination.
- Server-side: gate anonymous traffic at the edge, not in the Worker.