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.
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:
| Vercel | Render | |
|---|---|---|
| Execution model | Serverless functions, boot-per-request | Always-on processes, boot once |
| This app's Route Handlers (webhook, download) | Fits directly — bounded, stateless, no maxDuration needed | Would work, but pays for an always-on instance between requests |
| This repo's screenshot pipeline | Would need re-architecting around a function timeout it wasn't written for | Fits directly — long-running, persistent disk, no timeout to design around |
| State between invocations | None by default — every function starts cold | Disk persists across requests on the same instance |
| Where it actually runs today | Both Route Handlers, in production | Nowhere — 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
| Mistake | Symptom | Fix |
|---|---|---|
| Assuming a slow script will "just work" as a Vercel function | Function times out mid-run; a clone-and-boot pipeline needs minutes, not seconds | Long-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 occasionally | Paying for idle compute between a handful of webhook deliveries a day | A 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 elsewhere | Only 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 needs | A stateless webhook receiver on a platform priced for always-on compute, or a long-running worker forced into a function timeout | Read 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.
