Vercel edge network and functions
Vercel’s compute story has three layers stacked at progressively-deeper distance from the user: the Edge Network (126+ POPs, runs only routing + middleware + edge functions on V8 isolates), Vercel Functions (regional Node.js / Python — full-fat runtime, now defaulting to the Fluid Compute concurrency model), and Incremental Static Regeneration (cached HTML between them). The big 2024-2025 shift was Fluid Compute — Functions instances are now reused across concurrent requests, eliminating the strict one-instance-per-request model that defined AWS Lambda since 2014. For AI / I/O-bound workloads, Fluid often cuts costs 40-70% vs the legacy per-invocation pricing.
See also
- vercel-platform-and-deployments — how code reaches the network
- vercel-ai-sdk — streaming AI responses run on Functions, often in Edge runtime
- vercel-observability-and-monitoring — function performance metrics
- hidden-tricks-and-gotchas-vercel — middleware cost surprises, ISR pitfalls
- workers-runtime-and-deployment — the other major edge platform for comparison
1. The three runtime tiers
Per vercel.com/docs/functions/runtimes. Vercel exposes three primary runtimes:
| Runtime | Backed by | Cold start | Memory | Max duration | Use for |
|---|---|---|---|---|---|
| Edge | V8 isolates (on Cloudflare-compatible runtime) | ~5-50ms | 128 MB | 25s (streaming) / 30s | Middleware, low-latency redirects, lightweight APIs, geographic routing |
| Node.js (Fluid) | Node 22 in firecracker microVM with concurrent request handling | ~50-300ms first; ~0ms reused | Up to 4 GB | 60s (Hobby) / 800s (Pro) / 15min (Enterprise) | AI APIs, database access, business logic |
| Python | Python 3.12 in microVM | ~200-500ms | Up to 4 GB | Same as Node | Python-specific libs (numpy, pandas, ML libs) |
The retired-but-still-callable Serverless (non-Fluid) runtime is the legacy one-instance-per-request model. New projects default to Fluid. Existing projects can opt in via Project Settings → Functions → Fluid Compute.
2. Edge Network — the CDN layer
Per vercel.com/docs/cdn. The Vercel Delivery Network is a globally distributed CDN with:
- 126+ POPs across 51 countries (per
vercel.com/docs/regions). - 20+ regional compute regions where Functions actually run (e.g.
iad1= us-east-1 region). - Framework-aware caching — for supported frameworks (Next.js, SvelteKit, Nuxt, Astro), cache rules are derived from your routing config at build time, not hand-written.
- Always-on DDoS mitigation + WAF included on every deployment.
Request flow:
User → POP (closest of 126) → CDN cache check
↓ miss
→ Edge runtime (middleware + edge functions)
↓ if needs origin compute
→ Function region (regional)
↓ if needs upstream
→ Origin (your database / external API)
3. Routing Middleware — code at the edge
Per vercel.com/docs/routing-middleware. Middleware runs before the CDN cache check, at every POP. Use for:
- Auth (verify JWT, redirect to login).
- Geographic routing (different content per country).
- A/B testing (rewrite to a variant).
- Bot detection / rate limiting.
- Custom headers.
// middleware.ts (Next.js)
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
// Geo-block
const country = request.geo?.country ?? "US";
if (["CN", "RU"].includes(country)) {
return new NextResponse("Not available in your region", { status: 451 });
}
// Auth check
const token = request.cookies.get("auth-token");
if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url));
}
// Pass through
return NextResponse.next();
}
export const config = {
// matcher restricts which paths invoke middleware
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};Important: middleware runs on every request that matches, including cached responses. A poorly-scoped matcher means middleware runs on every static asset request — that’s expensive at the Edge Request tier. Always use a tight matcher.
Middleware runs in the Edge runtime — V8 isolate, no Node.js APIs (no fs, no native modules). Use fetch for any I/O.
4. Vercel Functions — request handlers
Per vercel.com/docs/functions. A function is a file that exports a request handler. The recommended shape (modern Vercel):
// api/hello.ts
export default {
async fetch(request: Request): Promise<Response> {
return new Response("Hello from Vercel!");
},
};Or framework-specific (Next.js App Router):
// app/api/hello/route.ts
export async function GET(request: Request) {
return new Response("Hello from Vercel!");
}
export async function POST(request: Request) {
const data = await request.json();
return Response.json({ received: data });
}Choosing the runtime per-function (Next.js App Router):
// app/api/chat/route.ts
export const runtime = "edge"; // V8 isolates — for streaming AI
// or
export const runtime = "nodejs"; // Node.js — defaultvercel.json config per-route:
{
"functions": {
"api/heavy.ts": {
"runtime": "nodejs22.x",
"memory": 3008,
"maxDuration": 300
},
"api/edge.ts": {
"runtime": "edge"
}
}
}5. Fluid Compute — the concurrency model
Per vercel.com/docs/fluid-compute. The headline change: a single function instance can handle multiple concurrent requests instead of one-per-instance (Lambda style). For I/O-bound work (AI inference, database queries), most of the request lifecycle is waiting — Fluid uses that idle time to process other requests on the same instance.
How it works:
- Each instance has configurable max concurrency (Vercel auto-tunes, typically 100-1000).
- When a request comes in, the router prefers an existing warm instance with capacity. Only spawns a new instance when all warm instances are at capacity.
- Idle instances are kept warm for ~5 minutes (drastically reducing cold starts).
- Billing is active CPU time + provisioned memory time — you pay for what you actually use, not request count × full max-duration.
// Fluid Compute uses standard async patterns — no special API
export async function POST(request) {
// While this awaits the OpenAI response, the instance can handle other requests
const result = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: { "Authorization": `Bearer ${process.env.OPENAI_API_KEY}` },
body: JSON.stringify({ ... }),
});
return result;
}Concurrency tuning: Vercel handles this automatically, but for CPU-bound work you can override:
{
"functions": {
"api/cpu-heavy.ts": {
"concurrency": 1 // disable concurrency for CPU-bound work
}
}
}6. Streaming responses
Per vercel.com/docs/functions/streaming-functions. Critical for AI / LLM use cases — let the model stream tokens as they’re generated rather than waiting for the full response.
// app/api/chat/route.ts
import { streamText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
export async function POST(request: Request) {
const { messages } = await request.json();
const result = streamText({
model: anthropic("claude-sonnet-4-6"),
messages,
});
return result.toDataStreamResponse();
}
export const runtime = "edge"; // Edge is best for streaming — lowest overhead
export const maxDuration = 25; // Edge max duration for streamingStreaming + Edge runtime is the canonical AI API shape on Vercel — minimum cold start, geographic distribution, no Lambda-style timeout issues for long streams.
7. Regions
Per vercel.com/docs/regions. Functions run in one specific region per project unless overridden. Default: iad1 (Washington, D.C., USA).
Region list (partial):
iad1— Washington, D.C., USApdx1— Portland, OR, USAsfo1— San Francisco, USAcdg1— Paris, Francefra1— Frankfurt, Germanylhr1— London, UKdub1— Dublin, Irelandbom1— Mumbai, Indiasin1— Singaporesyd1— Sydney, Australiagru1— São Paulo, Brazilhnd1— Tokyo, Japansjc1— San Jose, USAarn1— Stockholm, Swedenicn1— Seoul, South Korea
Pick a region close to your data source — for a function that queries a Postgres in AWS us-east-1, pick iad1 (same region). The 80ms latency of cross-region DB queries dominates everything else.
Multi-region functions (Enterprise) — same code runs in multiple regions; Vercel routes to the nearest. Worth it only if your data is also multi-region replicated.
8. Pricing — Fluid Compute
Per vercel.com/docs/functions/usage-and-pricing. Fluid Compute has three metering dimensions:
| Dimension | What it measures | Hobby (free) | Pro included | Overage |
|---|---|---|---|---|
| Active CPU time | Function actively executing JS / Python (not waiting on I/O) | Limited | 4 vCPU-hours | $0.128 / vCPU-hour |
| Provisioned memory time | Memory × wall-clock time the instance was alive | 100 GB-Hours | 360 GB-Hours | $0.0094 / GB-hour |
| Invocations | Function entry counts (not requests — invocations) | 100k | 1M | $0.6 / million |
Compared to legacy serverless (which billed per-invocation × max-duration), Fluid is dramatically cheaper for I/O-bound work. A function waiting 5s on an LLM and using minimal CPU costs ~10% of what it would have under legacy billing.
9. Limits
Per vercel.com/docs/functions/limitations:
| Limit | Edge | Node.js (Fluid) |
|---|---|---|
| Max bundle size (compressed) | 1 MB (Free), 4 MB (Pro) | 50 MB |
| Max bundle size (uncompressed) | 4 MB (Free), 8 MB (Pro) | 250 MB |
| Memory | 128 MB | 128 MB - 4 GB |
| CPU time per request | 50ms | unbounded within max duration |
| Max duration | 25s (streaming) / 30s | 60s (Hobby) / 800s (Pro) / 900s (Enterprise) |
| Concurrent connections to same function | Auto | Auto |
| Body size limit | 4.5 MB | 4.5 MB |
| Response size limit | 4.5 MB sync, unlimited streaming | 4.5 MB sync, unlimited streaming |
Edge’s 1MB code limit is a real constraint for AI apps. Many AI SDKs (LangChain) bundle to > 1MB on Edge. Workaround: use a smaller AI library (Vercel AI SDK is < 50KB), or move to Node.js runtime.
10. Incremental Static Regeneration (ISR)
Per vercel.com/docs/incremental-static-regeneration. ISR serves cached static HTML, regenerating in the background on a schedule or trigger.
Two regeneration modes:
10.1 Time-based revalidation
// Next.js App Router
export const revalidate = 60; // re-generate at most once every 60 seconds
export default async function Page() {
const data = await fetch("https://api.example.com/data").then(r => r.json());
return <div>{data.headline}</div>;
}10.2 On-demand revalidation
Trigger a revalidation from any Function (e.g. webhook from CMS):
import { revalidatePath, revalidateTag } from "next/cache";
export async function POST(request: Request) {
await verifyWebhookSignature(request);
revalidatePath("/products"); // revalidate one path
revalidateTag("products"); // revalidate everything tagged "products"
return Response.json({ revalidated: true });
}ISR is request-collapsing — if 1000 users hit /products at the same moment after the cache expires, only one regeneration runs and all 1000 get the result. No thundering herd.
11. Image Optimization
Per vercel.com/docs/image-optimization. Vercel’s image pipeline:
- Resize / crop / convert (WebP / AVIF) on the fly.
- Cache transformed images on the CDN.
- Use
<Image>component (Next.js) or_next/imageURLs directly.
Defaults: 90% quality, WebP. Set quality={...} for less compression. Image URLs include a hash so cache busts on source change.
Pricing: $5 / 1k images optimized (overage). Hobby includes 1000 source images / month.
Image optimization at the edge (re:Invent / 2024) — most transforms happen at the POP, not the origin region. Latency on uncached images is ~100-300ms; cached images serve in < 50ms.
12. Caching tiers
Per vercel.com/docs/caching. Three layers:
| Layer | Where | TTL | Granularity |
|---|---|---|---|
| CDN cache | At POPs globally | Set by Cache-Control headers or framework rules | Per URL |
| Runtime cache | Inside the Function instance | Set explicitly | Per fetch / per query |
| ISR cache | At POPs + origin | Set by revalidate | Per page |
The runtime cache (re:Invent 2024) is a new layer — it caches fetch() results, DB query results, and any function inside the warm function instance:
import { unstable_cache } from "next/cache";
const getUser = unstable_cache(
async (id) => db.users.findUnique({ where: { id } }),
["user"],
{ revalidate: 60, tags: ["users"] }
);When the same function instance handles multiple requests (Fluid Compute), the cache shortens calls dramatically.
13. Edge Config — for ultra-low-latency reads
Per vercel.com/docs/edge-config. A globally-replicated read-only-ish data store accessible from Middleware and Functions. Reads return in < 1ms at the median (cache-local in the POP). Writes propagate in a few seconds.
import { get } from "@vercel/edge-config";
// In middleware or edge function
const featureFlag = await get("new-checkout-enabled");Use for: feature flags, A/B test config, redirects, blocklists. Don’t use for: anything that changes often, anything per-user, anything > 512 KB.
See vercel-storage-and-data for details.
14. WebSockets — coming soon
As of 2026-05-25, Vercel Functions don’t natively support WebSocket connections (Edge or Node.js). The workaround is to use a third-party WebSocket provider (Pusher, Ably, Liveblocks, Soketi) called from your Functions.
SSE (Server-Sent Events) works — that’s what streamText uses for AI streaming. SSE is one-way (server → client) which covers most streaming use cases. For bidirectional, use a hosted service.