When I first rebuilt this website with Next.js, Cloudflare Pages was a straightforward way to publish it. Since then, the deployment has changed: my site now runs on Cloudflare Workers, with OpenNext adapting Next.js for Cloudflare's runtime.
The move gives my site a proper Next.js server runtime, while Cloudflare can still serve the generated static assets. It also brings a limit I hadn't accounted for: the Worker uses more CPU time per request than the Free plan allows.
Why move to Workers?
Next.js can produce static files, but it can also run server-side code to handle requests. I wanted to run my site using Cloudflare's Workers runtime rather than rely only on a static export. OpenNext provides the adapter that builds the Next.js application into a Worker deployment.
I now build and deploy my site with OpenNext and Wrangler. The deployment uses an R2 bucket for Next.js's incremental cache, which helps preserve cached data between Worker instances.
This setup gives the application a server runtime when it needs one, without treating every page asset as server-rendered content. Static assets can still be served separately; the CPU concern is about requests that actually execute the Worker.
The 10ms limit
Cloudflare measures CPU time separately from the total time a visitor waits. Time spent waiting for network requests does not count as CPU time; the metric measures time spent executing the Worker code.
On the Workers Free plan, the CPU limit is 10ms per invocation. In the Worker metrics for this site, a request is using around 40 ms of CPU time. That is roughly four times the Free plan limit, so the Free plan is not enough for the current workload. A request that consistently exceeds its configured CPU limit can be terminated rather than completing normally.
That does not mean every visit to the website uses 40 ms of Worker CPU. Static assets that are served without invoking the Worker do not use that request's CPU budget. The measurement matters for requests that run the Next.js application, such as server-rendered pages or other dynamic handling.
What this means for cost
The straightforward option is the Workers Paid plan. At the time of writing, it starts at $5 USD per month and includes a higher CPU limit per invocation, as well as included monthly request and CPU usage. CPU time above the included allowance can incur additional usage charges, so the plan price is a starting point rather than a guarantee that every workload costs exactly $5.
I could also investigate whether the Worker can do less work for each request. Profiling and reducing server-side work may bring CPU usage down, and caching suitable responses could avoid running the application on some visits. But I don't want to assume that caching will solve the issue without checking which routes are dynamic and measuring the result.
Was the migration worth it?
For this site, moving to Workers is a better fit for running the Next.js application with OpenNext. The trade-off is that the Worker has a real per-invocation CPU budget, and my current measurements are well above the Free plan's 10ms allowance.
The main lesson is to check CPU usage early when deploying a server-rendered Next.js application to Workers. A build that deploys successfully is not the whole story: the runtime plan has to support the work the application performs on real requests. For now, the Free plan isn't suitable for my site's measured workload, so I'll need to use Workers Paid or reduce the CPU time before relying on it.
For the current figures, see Cloudflare's Workers limits and Workers pricing documentation.