Skip to main content
ASoc
Comparison

Vercel vs. Render: Does Anything You Run Need to Stay Running?

Two Route Handlers, both stateless, neither overriding the default timeout — versus a release script that clones, installs, and boots for minutes. That split is the real decision axis.

The ASoc Team10 min read

Vercel runs short, stateless functions per request; Render runs long-lived processes with a persistent disk. The question isn't which platform is faster — it's whether anything in your app needs to keep running, or keep local state, between requests. This storefront's production code is entirely the first kind: two Route Handlers, both nodejs runtime, neither declaring a timeout override because neither needs one. Its release tooling is the second kind, and deliberately never runs as a hosted service on either platform.

What this storefront actually deploys

Production has exactly two Route Handlers, both explicit about their runtime:

// src/app/api/webhooks/lemonsqueezy/route.ts
export const runtime = "nodejs";

// src/app/api/download/route.ts
export const runtime = "nodejs";

Neither file sets maxDuration — the Vercel-specific export that raises a function's timeout past the platform default. Zero routes in this codebase override it, because both handlers do exactly one thing per invocation (verify a webhook signature and write to Postgres; check entitlement and mint a short-lived signed URL) and return well inside any default window. That's the shape Vercel's serverless model is built around: a request arrives, a function boots, does bounded work, returns, and the instance can vanish. Server Actions vs. API Routes covers why these two specifically had to be Route Handlers rather than Server Actions; this post is about the hosting model underneath both, not the choice between them.

What Render adds that this app doesn't use

Render's pitch is the opposite shape: web services with responses that can run up to 100 minutes, background workers that run continuously rather than per-request, cron jobs, and persistent disks that survive a redeploy. All of that solves a real problem — a process that needs to hold state in memory across many requests, or one that just takes longer than a request should reasonably take.

This codebase has exactly that kind of long-running process, and it isn't a production route:

scripts/release/capture-screenshots.mjs

This clones a template's edition repository, runs an install, boots the dev server, and drives a headless browser through screenshot crops — on disk, with node_modules running 300–900 MB per clone, and a runtime measured in minutes rather than the sub-second budget a serverless function is built for. The screenshot-pipeline post has the full mechanics; the fact worth pulling out here is what it implies about hosting. A process shaped like that — long-running, stateful on disk mid-run, tolerant of taking minutes — is exactly Render's web-service or background-worker model, not Vercel's. And it runs as neither: it's a script invoked by a person or a CI job, never deployed as a hosted service on any platform, because a release happens a few times a year and paying for either platform's compute to sit idle between releases would be solving a problem this repo doesn't have.

That split is the actual decision axis, not a benchmark:

VercelRender
Execution modelServerless functions, boot-per-requestAlways-on processes, boot once
This app's Route Handlers (webhook, download)Fits directly — bounded, stateless, no maxDuration neededWould work, but pays for an always-on instance between requests
This repo's screenshot pipelineWould need re-architecting around a function timeout it wasn't written forFits directly — long-running, persistent disk, no timeout to design around
State between invocationsNone by default — every function starts coldDisk persists across requests on the same instance
Where it actually runs todayBoth Route Handlers, in productionNowhere — the pipeline is a script, run manually or in CI, hosted on neither platform

The question that actually decides it

"Which is faster" and "which is cheaper" both depend on traffic patterns this post can't measure for your app. The question this codebase's own split answers cleanly is narrower: does anything in the app need to keep running, or keep state on disk, between requests? For the two things that are actually deployed here, the answer is no — a webhook fires, gets verified, writes a row, and the function is done; a download request checks entitlement and redirects. Neither needs a second request's memory of the first. The one process in this repo that would answer yes to that question is release tooling nobody deploys, which is itself informative: the presence of a long-running script in a codebase isn't automatically a reason to provision a platform for it if the script only ever needs to run on demand.

Where Render is the straightforwardly better answer

  • A backend with genuinely long-lived connections — a WebSocket server, a queue consumer, anything that holds a connection open rather than completing a request-response cycle.
  • A process that needs the same disk across runs — a build cache, a local SQLite file, anything that would otherwise be re-fetched or rebuilt every cold start.
  • Predictable, flat pricing for a workload that runs continuously — an always-on worker costs the same whether it's busy or idle, which is a feature once utilization is high enough to make Vercel's usage-based model the more expensive option.

None of those describe this storefront's production surface. They do describe what its release tooling would need if it were ever promoted from a script to a scheduled service — which hasn't happened, and the reasoning above is exactly why it hasn't needed to.

Mistakes and how they show up

MistakeSymptomFix
Assuming a slow script will "just work" as a Vercel functionFunction times out mid-run; a clone-and-boot pipeline needs minutes, not secondsLong-running work belongs on a platform built for it (Render, a VM, a scheduled job runner) — or stays a manual/CI script, as it does here
Provisioning an always-on Render worker for a route that only fires occasionallyPaying for idle compute between a handful of webhook deliveries a dayA bursty, stateless handler is what serverless functions exist for — no idle cost between invocations
Adding maxDuration everywhere "to be safe"Every route silently allowed to run longer, masking a handler that should have been fixed or moved elsewhereOnly override the default when a specific, understood handler needs it — this codebase's two Route Handlers never have
Choosing a host before checking what the code actually needsA stateless webhook receiver on a platform priced for always-on compute, or a long-running worker forced into a function timeoutRead what each route or script does — bounded and stateless vs. long-running and stateful — before picking where it runs

FAQ

Is Vercel or Render better for a Next.js app? Depends on what the app's own routes need, not on the framework. A Next.js app whose server code is all short Route Handlers and Server Actions — this storefront's shape — fits Vercel's model directly. One with a long-running backend process bolted on fits better split across both, or hosted entirely on Render.

Can Render run a Next.js app at all? Yes — Render supports Node web services generally, including a Next.js server. What it doesn't give you that's specific to Vercel is the same framework-aware deploy pipeline (ISR, the ƒ/ route table, edge middleware wired in automatically) — you're running a Node process yourself rather than deploying Next.js's own build output format.

Does this storefront use any Render-shaped infrastructure at all? No — everything in production is Vercel-hosted. The release-tooling comparison above is a "what if" against the one process in this repo whose shape doesn't match Vercel's model, to make the actual decision axis concrete rather than abstract.

How do I know if my own app needs Render's model instead of Vercel's? Check whether any of your server code needs to hold state or a connection open across more than one request, or routinely runs longer than a few seconds. If every Route Handler and Server Action completes fast and stateless — as both of this app's do — you don't need it.

Templates in this post

ASoc Pip is a forex-trading marketing site with a live-rates dashboard preview — the kind of product where a real live-rates feed (not this template's static preview) would be the long-lived-connection case Render is built for. ASoc Quest is a dark-theme games-storefront landing page with deals and top-seller rows, running the same stateless-request shape as this storefront's own production routes. ASoc Quill is a personal blog template for makers — a static-content-first site where neither platform's compute layer sees much use at all.

Browse the full sets: Next.js landing page templates, Tailwind landing page templates. For why this app's two Route Handlers couldn't be Server Actions, see Server Actions vs. API Routes; for the screenshot pipeline referenced above in full, Automated Screenshots with Playwright.

Keep reading

Comparison8 min read

Vite vs. Vue: A Build Tool and a Framework Are Not Alternatives

Vite builds, Vue renders, and most projects use both. The comparison that resolves is Vite vs. Vue CLI — plus which layer to blame when a build breaks.

Read more
Comparison9 min read

Vite vs. Webpack: 488 Pages Built by a Repo Running Neither

Most comparisons assume you pick a bundler. On a framework you inherit one — and the two config lines this codebase writes anyway are where the real difference shows.

Read more
Comparison8 min read

Vitest vs. Jest: 11 Lines of Config and 331 Tests in 2.48s

Measured on this repo: 31 files, 331 tests, 2.48s — and the `environment 3ms` line that explains most of the Vitest-versus-Jest gap.

Read more