Migrating from Vercel to Cloudflare Workers Without Breaking Everything
The gotchas, the wins, and the parts of your app that will need rewriting no matter how confident the marketing sounds.
The Vercel bill grew, the marginal-request cost math stopped working, and now you're staring at Cloudflare Workers wondering how bad the migration is. Migrating from Vercel to Cloudflare Workers is doable — but there's real work between "it deploys" and "it runs everything the old app did."
This is the honest version of that migration, from projects we've either done or watched teams do.
Why teams move
Three reasons come up over and over:
- Cost at scale. Cloudflare's per-request pricing is meaningfully cheaper past a certain volume.
- Global latency. Workers run in more locations, closer to more users.
- Bundled primitives. D1, R2, KV, Queues, Durable Objects live in the same platform with tight integration.
And one reason you shouldn't move: because a Twitter thread said Vercel is bad. Both platforms are excellent. The right one depends on your app.
The parts that just work
Static assets, image optimization, edge-side redirects — these translate cleanly. If your app is a marketing site plus a few API routes, migration is a weekend.
Modern frameworks with Cloudflare adapters (TanStack Start, SvelteKit, Astro, Remix) also translate cleanly. The framework does most of the platform-specific work for you.
The parts that break
Node built-ins
Workers runs on workerd, not Node. Even with nodejs_compat enabled, several modules are stubs. child_process, fs.watch, sharp, native binaries — all non-functional in the Worker. Any code that spawns processes or writes to arbitrary paths has to go.
The failure mode is subtle. The import succeeds. The call throws at runtime. Test in a production build, not wrangler dev.
File uploads and image processing
If you're using sharp to resize on the server, that's out. Cloudflare Images handles this natively — different API, cheaper at scale. Rewrite required.
Long-running requests
Workers has a 30-second CPU limit on paid plans, more forgiving than the wall-clock limit on Vercel Hobby but still a hard cap. Anything that streams for longer needs to move to Durable Objects, Queues, or a real worker. Related reading: How to run long jobs on serverless.
Middleware that assumes Node.js APIs
Vercel's edge middleware is close to the Web standard. If you've been using Node-flavored middleware in serverless functions, expect rewrites.
Database connections
Direct Postgres connections from a Worker are limited. Hyperdrive, connection poolers (PgBouncer, Supavisor), or edge-friendly clients are required. Long-lived TCP connections don't fit the Worker model.
Next.js is a special case
OpenNext for Cloudflare has matured a lot, and most Next.js apps can now deploy to Workers. But: not every feature works, not every middleware pattern translates, and image optimization defaults change. Budget more time than you'd think.
If your app is deeply Next.js-idiomatic (ISR, middleware everywhere, tight edge/node function distinctions), this is the biggest chunk of migration work.
The migration order we use
1. Audit dependencies
Run through package.json. Flag anything that mentions "Node.js only," ships native binaries, or spawns processes. Find replacements before touching the platform.
2. Move static assets and the marketing surface first
Low-risk, high-visibility. Confirms your DNS, TLS, and cache-control assumptions.
3. Migrate API routes in slices
Route by route, or feature by feature. Use Cloudflare's ability to route different paths to different origins — you can have half the app on each platform during the migration.
4. Move background work
Queues, cron, webhooks. Test idempotency thoroughly — duplicate deliveries during the migration are common.
5. Cut over the primary domain
Only after every route has been tested at production scale on Workers.
Mistakes we've seen
Big-bang cutovers. Something always breaks that only shows up under real traffic. Slice the migration.
Assuming wrangler dev matches production. It runs on a local Node process, not workerd. Test against a preview deployment before shipping.
Ignoring the vendor lock-in axis. You're not becoming platform-independent by migrating. You're trading one dependency for another. Pick the one you'd rather be locked into.
What you gain
- Cheaper compute past a real usage threshold.
- D1, R2, KV, Queues, Durable Objects — a database, object store, key-value, queue, and durable state as first-party primitives.
- Consistent global latency without configuring regions.
What you lose
- Preview URLs with Vercel's polish. Cloudflare Pages has them, but the DX gap is real.
- The "click deploy" ecosystem of Vercel integrations.
- Some Next.js features. Depends on your app.
Common misconceptions
"Workers doesn't run Node." With nodejs_compat, most Node stdlib works. Native modules and process-spawning code don't.
"It's the same platform, just cheaper." The pricing models are structurally different. Cheap at high volume, comparable at low volume, more painful for CPU-heavy work.
"OpenNext handles everything." It handles a lot. Read the compatibility list before you commit.
FAQ
How long does a real migration take?
For a typical Next.js SaaS with a database, background jobs, and a few third-party integrations: two to six weeks. The variance is almost entirely about Node.js-only dependencies.
Can I run half on each?
Yes, during migration. Long-term, pick one — running production traffic across two platforms doubles your on-call surface.
Is D1 production-ready?
Yes, for the workloads it's designed for (small-to-medium, read-heavy). For write-heavy multi-region, Postgres via Hyperdrive is still safer. Related reading: Postgres vs SQLite for small apps.
Where to go from here
If you're weighing the migration and want a second opinion on scope before committing engineering time, our AI Audit covers infrastructure migrations too — two weeks, honest scope, no upsell.
FAQ
Frequently asked questions
Should I migrate for the cost savings alone?+
Only if your bill is meaningfully painful. Below $2-3k/month on Vercel, migration engineering time is probably worth more than the savings.
Do I need to rewrite my API routes?+
Rarely. Framework adapters handle most translation. Node.js-specific dependencies are what force rewrites.
What breaks silently in production?+
Anything using <code>child_process</code>, <code>sharp</code>, or native bindings. Test a production build against real traffic before cutover.
Building something similar?
Let's talk in 30 minutes.

