Skip to main content
Back to the notebook

Engineering

Running Next.js 16 on Cloudflare Workers: what actually breaks

OpenNext gets you 90% of the way. The last 10% — patch scripts, Node APIs, and edge quirks — is where the real work lives.

Both Horrorometer and SENDadvisor run Next.js 16 on Cloudflare Workers via @opennextjs/cloudflare. Every time I tell someone that, they ask the same question: "Does that actually work?"

The honest answer: yes, but you earn it. Here’s what actually breaks, and what I did about each thing.

Why bother

The pitch is simple. Cloudflare Workers gives you a single deployment target that’s fast everywhere, with D1 (SQLite at the edge), R2 (S3-compatible storage), KV, Durable Objects, and Analytics Engine all in one account. No container orchestration, no regional routing decisions, and a free tier generous enough to run a real product on.

For a solo builder, one platform for compute, storage, cache, and analytics is a massive reduction in operational surface. That’s why I accepted the friction.

What breaks

1. The Node runtime isn’t the Node runtime

Workers run on a V8 isolate, not a Node process. Strict ES modules, no require, no fs, no path in the places you expect them, and a bunch of APIs that exist but behave subtly differently. Next.js assumes Node. “Assumes” is doing a lot of work in that sentence.

The fix: OpenNext, which transpiles the server into a Worker-compatible format. It handles the big stuff — routing, rendering, the request lifecycle. It does not handle every package in your dependency tree.

2. Patch scripts: the unglamorous glue

Three separate post-build patch scripts keep Horrorometer running. Each one rewrites some assumption the framework makes about its runtime — file access patterns, module resolution, an API that exists in Node but not in workerd. I’m not going to pretend they’re elegant. They are, however, small, testable, and the difference between a build that deploys and a build that boots.

The lesson: budget for patch scripts. When you’re on a new runtime with a framework that targets an older one, the gap lives in post-build fixes and nobody warns you about this in advance.

3. Local dev lies to you

Wrangler dev mode is good, but it’s a simulation, not the real thing. Differences I’ve hit: timing behaviours under load, minor differences in how headers are handled, and — most annoyingly — packages that work in local dev and fail in production because the dev shim provides an API workerd doesn’t.

The fix: smoke tests after every deploy, in production, before promotion. Not "I deployed it", but "I deployed it and confirmed the critical paths return 200".

4. Bundle size and cold starts

A Next.js server bundle in a Worker has size limits and cold-start characteristics that are different from a Node process on a long-running server. Route splitting helps. Keeping heavy dependencies out of the server runtime helps more — push what you can to the client, keep the server lean.

What works better than you’d expect

  • Static generation + edge caching is the sweet spot. Content-rendered pages from D1, cached at the edge, revalidated by events — fast and cheap.
  • D1 is genuinely fine for read-heavy workloads. Both products lean on it heavily and neither has felt the ceiling.
  • Durable Objects (Kanaboards) give you coordination primitives that would otherwise require Redis plus a lot of hand-rolled wiring.
  • One deploy artifact for app, API, and (in Kanaboards’ case) MCP endpoint simplifies pipelines more than you’d expect.

Would I do it again?

Yes — with my eyes open. The pattern I’d recommend: start on Workers only if you’re happy reading source code when things break, and put the deploy pipeline (build → patch → deploy → smoke test) in place before you write features. The framework will catch up eventually; until then, the patch scripts are load-bearing.

Keep reading

More from the blog.

Accessibility

Accessible by default: how I bake it in from the first sketch

Accessibility isn’t a checklist at the end — it’s a posture. Here’s how I keep it present from wireframe through to production code.

6 min read
Product

Shipping solo: what I learned building three live products

Horrorometer, Kanaboards, and SENDadvisor all started as itches I wanted to scratch. Some patterns held across all three — and a few surprised me.

8 min read
UX

Designing for stressful systems: lessons from SENDadvisor

Families navigating SEND services are already overwhelmed. How do you design a directory that feels calm, trustworthy, and genuinely useful?

7 min read