Deploying Next.js to Cloudflare Workers with OpenNext

January 20, 2024

Complete Guide to Deploying Next.js on Cloudflare Workers

Introduction

Cloudflare Workers is a powerful edge computing platform that can run your application code across hundreds of data centers worldwide. With the release of OpenNext's Cloudflare adapter, you can now deploy complete Next.js applications to Cloudflare Workers, enjoying the ultimate performance that edge computing provides.

This article will detail how to deploy Next.js projects to Cloudflare Workers using the @opennextjs/cloudflare adapter (Note: this is a different deployment method from Cloudflare Pages).

What is @opennextjs/cloudflare?

@opennextjs/cloudflare is the Cloudflare adapter for the OpenNext project. It converts Next.js build output into a format that can run on Cloudflare Workers. This adapter uses Cloudflare's Node.js compatibility layer, allowing Next.js applications to execute in Workers' workerd runtime.

Supported Features

  • ✅ Static Site Generation (SSG)
  • ✅ Server-Side Rendering (SSR)
  • ✅ Incremental Static Regeneration (ISR)
  • ✅ API Routes
  • ✅ Middleware
  • ✅ Image Optimization
  • ✅ Next.js 14 and 15

Prerequisites

Before you begin, ensure you have:

  • Node.js 18 or higher
  • npm, pnpm, or yarn
  • A Cloudflare account
  • An existing Next.js project (or ready to create a new one)

Option 1: Create a New Next.js Project (Recommended for Beginners)

Using create-cloudflare CLI

Cloudflare provides an official CLI tool for quick project setup:

npm create cloudflare@latest my-next-app -- --framework=next --platform=workers

This command will:

  1. Create a new Next.js project
  2. Automatically configure the @opennextjs/cloudflare adapter
  3. Generate necessary configuration files
  4. Install all dependencies

After creation, navigate to the project directory:

cd my-next-app

Option 2: Add Cloudflare Workers Support to Existing Project

Step 1: Install Dependencies

In your Next.js project root directory, run:

# Using npm
npm install @opennextjs/cloudflare@latest
npm install --save-dev wrangler@latest

# Or using pnpm
pnpm install @opennextjs/cloudflare@latest
pnpm install --save-dev wrangler@latest

# Or using yarn
yarn add @opennextjs/cloudflare@latest
yarn add --dev wrangler@latest

Step 2: Configure package.json

Add the following scripts to package.json:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "build-open": "opennextjs-cloudflare build",
    "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
    "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
    "upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload",
    "cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
  }
}

Command explanations:

  • build: Standard Next.js build command
  • build-open: Build using OpenNext adapter
  • preview: Preview Worker locally
  • deploy: Deploy to Cloudflare (goes live immediately)
  • upload: Upload new version (for gradual rollouts)

Step 3: Configure next.config.js

Modify next.config.js to support Cloudflare development environment:

import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";

// Initialize only in development environment
if (process.env.NODE_ENV === "development") {
  await initOpenNextCloudflareForDev();
}

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Your other configurations...
};

export default nextConfig;

Step 4: Create wrangler.jsonc Configuration File

Create a wrangler.jsonc file in your project root:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-nextjs-app",
  "main": ".open-next/worker.js",
  "compatibility_date": "2024-12-30",
  "compatibility_flags": [
    "nodejs_compat",
    "global_fetch_strictly_public"
  ],
  "assets": {
    "directory": ".open-next/assets"
  }
}

Important Configuration Notes:

  • compatibility_date: Must be set to 2024-09-23 or newer for process.env to work properly
  • nodejs_compat: Must be enabled for Node.js API compatibility
  • main: Points to the built Worker entry file
  • assets: Static assets directory

Step 5: Create open-next.config.ts (Optional)

If you need to use R2 for caching, create open-next.config.ts:

import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";

export default defineCloudflareConfig({
  incrementalCache: r2IncrementalCache,
});

Local Development and Testing

Development Mode

Use the standard Next.js development server:

npm run dev

Access in browser at http://localhost:3000.

Preview Worker

Test your application in the local Workers runtime:

npm run preview

This will:

  1. Build your application
  2. Start a local server in the workerd runtime
  3. Simulate the production environment

Important: Preview mode is closer to production and should be run before deployment.

Deploy to Cloudflare Workers

Method 1: Deploy from Local

First, login to Cloudflare:

npx wrangler login

Then deploy:

npm run deploy

On first deployment, you'll be prompted to select your Cloudflare account and create a Worker name.

Method 2: Using Workers Builds (CI/CD)

Workers Builds is Cloudflare's continuous integration service that can automatically build and deploy.

Step 1: Push Code to Git

Ensure your code is pushed to GitHub or GitLab.

Step 2: Configure in Cloudflare

  1. Login to Cloudflare Dashboard
  2. Go to "Workers & Pages"
  3. Click "Create application"
  4. Select "Workers"
  5. Select "Connect to Git"
  6. Authorize and select your repository

Step 3: Configure Build Settings

In build configuration, set:

  • Framework preset: Choose "None" or "Next.js"
  • Build command: npm run build-open or npx opennextjs-cloudflare build
  • Build output directory: .open-next

Step 4: Add Environment Variables

In "Settings" > "Environment variables", add:

NODE_VERSION=18

Plus any other environment variables your application needs.

Step 5: Deploy

After saving the configuration, Cloudflare will automatically trigger the first build and deployment.

Configure Caching (Using R2)

For better performance, it's recommended to configure an R2 bucket for ISR caching.

Create R2 Bucket

npx wrangler r2 bucket create next-cache

Bind in wrangler.jsonc

{
  "r2_buckets": [
    {
      "binding": "NEXT_INC_CACHE_R2_BUCKET",
      "bucket_name": "next-cache"
    }
  ]
}

Configure open-next.config.ts

import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";

export default defineCloudflareConfig({
  incrementalCache: r2IncrementalCache,
});

Using Cloudflare Bindings

Cloudflare Workers can access various services through bindings.

Configure KV Storage

Add in wrangler.jsonc:

{
  "kv_namespaces": [
    {
      "binding": "MY_KV",
      "id": "your-kv-namespace-id"
    }
  ]
}

Use in Next.js

Access in API Routes

// app/api/data/route.ts
import { NextRequest } from "next/server";

export const runtime = "edge";

export async function GET(request: NextRequest) {
  // Get bindings from environment
  const env = process.env as any;
  const value = await env.MY_KV.get("mykey");
  
  return Response.json({ value });
}

Access in Server Components

// app/page.tsx
import { getCloudflareContext } from "@opennextjs/cloudflare";

export default async function Home() {
  const { env } = await getCloudflareContext();
  const data = await env.MY_KV.get("mykey");
  
  return <div>{data}</div>;
}

Environment Variable Management

Local Development

Create a .dev.vars file (similar to .env.local):

API_KEY=dev-api-key
DATABASE_URL=http://localhost:3000

Production Environment

Set via Wrangler:

npx wrangler secret put API_KEY

Or configure in Cloudflare Dashboard under "Settings" > "Variables and Secrets".

Limitations and Considerations

1. Node.js Runtime Only

The adapter currently only supports Next.js Node.js runtime, not Edge Runtime. If your code has:

export const runtime = "edge";

You need to remove this line or change it to:

export const runtime = "nodejs";

But don't worry - even using Node.js runtime, your code still runs on global edge nodes.

2. CPU Time Limits

Cloudflare Workers has CPU time limits:

  • Free plan: 10ms CPU time
  • Paid plan: 30ms CPU time (configurable higher)

3. Memory Limits

Workers memory limit is 128MB - avoid memory-intensive operations.

4. Unsupported Features

  • Some next/image optimization features may be limited
  • Certain Node.js native modules may be incompatible
  • File system operations are restricted

Performance Optimization Tips

1. Enable ISR

Use Incremental Static Regeneration to balance performance and content freshness:

export const revalidate = 3600; // Revalidate every hour

2. Use Edge Caching

Set cache headers in API routes:

export async function GET() {
  return new Response(JSON.stringify(data), {
    headers: {
      "Cache-Control": "public, s-maxage=3600",
      "Content-Type": "application/json",
    },
  });
}

3. Optimize Bundle Size

Workers have bundle size limits - analyze with:

npx wrangler deploy --dry-run --outdir=dist

4. Use Cloudflare Images

For image optimization, use Cloudflare Images service instead of Next.js built-in image optimization.

Monitoring and Debugging

View Real-time Logs

npx wrangler tail

View Deployment Logs

Check in Cloudflare Dashboard at "Workers & Pages" > your project > "Deployments".

Analyze Performance

Use Cloudflare's Analytics panel to view:

  • Request volume
  • Error rate
  • P50/P99 latency
  • CPU usage time

Gradual Rollouts

Use the upload command instead of deploy to create a new version without immediately activating it:

npm run upload

Then manually control traffic switching percentages in Cloudflare Dashboard:

  • 0.05% → 0.5% → 3% → 10% → 25% → 50% → 75% → 100%

This allows safe testing of new versions with quick rollback if issues are found.

Custom Domains

In Cloudflare Dashboard:

  1. Go to your Worker project
  2. Click "Settings" > "Domains & Routes"
  3. Add your custom domain
  4. Configure DNS records (if domain is on Cloudflare)

Troubleshooting

Build Failure: wrangler.jsonc Not Found

Ensure the file is in the project root, or run:

npm run build-open

It will automatically generate the default configuration.

Empty Environment Variables

Check that compatibility_date in wrangler.jsonc is 2024-09-23 or newer.

Memory Exceeded

Optimize your code to reduce memory usage, or consider moving some logic to external APIs.

Node.js API Incompatibility

Check Cloudflare's Node.js compatibility list, use supported APIs or find alternatives.

Complete Example Project

Here's a minimal project structure:

my-nextjs-app/
├── app/
│   ├── api/
│   │   └── hello/
│   │       └── route.ts
│   ├── layout.tsx
│   └── page.tsx
├── next.config.js
├── wrangler.jsonc
├── open-next.config.ts
├── package.json
├── .dev.vars
└── .gitignore

package.json

{
  "name": "my-nextjs-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "build-open": "opennextjs-cloudflare build",
    "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
    "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy"
  },
  "dependencies": {
    "next": "^15.0.0",
    "react": "^18.3.0",
    "react-dom": "^18.3.0",
    "@opennextjs/cloudflare": "latest"
  },
  "devDependencies": {
    "wrangler": "latest",
    "typescript": "^5",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18"
  }
}

Comparison with Other Platforms

Cloudflare Workers vs Vercel

FeatureCloudflare WorkersVercel
Global Edge Network✅ 300+ data centers✅ Limited edge nodes
Cold Start TimeExtremely fast (< 10ms)Fast
Pricing ModelPay per requestPer request and bandwidth
Free Tier100,000 requests/day100GB bandwidth
Next.js IntegrationRequires adapterNative support
DX (Developer Experience)GoodExcellent

Cloudflare Workers vs Pages

FeatureWorkersPages
Deployment MethodCLI/CIGit integration
FlexibilityHighMedium
Static Asset HandlingRequires configAutomatic
Function ExecutionFull controlLimited
Gradual Rollouts

Summary

Deploying Next.js projects on Cloudflare Workers through the @opennextjs/cloudflare adapter has become very mature and reliable. The main advantages of this deployment method:

Advantages

  • Global Edge Deployment: Code runs in data centers closest to users
  • Ultra-low Latency: Cold start time is negligible
  • Cost Effective: Generous free tier with pay-as-you-go pricing
  • Powerful Ecosystem: Deep integration with KV, R2, D1, and other Cloudflare services
  • Gradual Rollouts: Safely test and release new versions

Suitable Scenarios

  • Global applications requiring low latency
  • High-traffic websites needing edge caching
  • API-intensive applications
  • Projects requiring Cloudflare ecosystem integration

Not Suitable For

  • Long-running background tasks
  • Heavy file system operations
  • Dependencies on specific Node.js native modules

With this guide, you should be able to successfully deploy your Next.js project to Cloudflare Workers and fully leverage the advantages of edge computing. Happy deploying!

Reference Resources