Claude Fable 5.1 & GPT-6 Astra packages are live

Vercel

Free

Deploying to Vercel — environment configuration, serverless and edge constraints, connection pooling, caching, preview deployments and cost control.

198 lines8.7 KB Kimi DevOps
targetModels
Kimi K3Kimi K2.6Kimi K2 FamilyFuture Kimi Models
name
vercel
category
DevOps
description
Deploying to Vercel — environment configuration, serverless and edge constraints, connection pooling, caching, preview deployments and cost control.
license
MIT
author
Agent.md maintainers
last-verified
reviewed-by
unreviewed
<!-- Generated from models/_canonical by scripts/build-model-variants.js. Edit the canonical source, not this file. Behavioural profile for Kimi: scripts/model-profiles.json -->

#Scope contract

FILE_ISOLATION: Modify only files inside the scope the task names; report any out-of-scope change instead of making it.


#Purpose

Rules for running an application on Vercel. The platform removes most deployment work; what remains is the set of constraints its execution model imposes — statelessness, cold starts, connection limits, and a build step that inlines values you may not want inlined.

Framework specifics are Backend/nextjs and Frontend/nextjs.


#Environment variables: build-time versus runtime

PrefixAvailable inBaked into the client bundle
NEXT_PUBLIC_*Browser and serverYes, at build time
Everything elseServer onlyNo
  • Never prefix a secret with NEXT_PUBLIC_. It is inlined into JavaScript served to every visitor, and rotating it requires a rebuild.
  • NEXT_PUBLIC_* values are captured at build time. Changing one in the dashboard does nothing until a redeploy — a recurring source of "I changed it and nothing happened".
  • Scope variables per environment (Production, Preview, Development). A preview deployment holding production credentials means every pull request can write to production.
  • Validate all variables at startup and fail the build or boot on a missing one. → DevOps/environments

#Serverless constraints

Every request may hit a cold instance. There is no shared process state.

Assumption that breaksRealityFix
In-memory cachePer-instance, discarded constantlyRedis / KV
In-memory rate limiterResets per instanceShared store → API/rate-limiting
Background work after the responseInstance is frozenwaitUntil, or a queue
Long-running requestHard execution limitBackground job → Backend/background-jobs
Local filesystem writesRead-only except /tmp, ephemeralObject storage
Cron in-processNot running between requestsVercel Cron
WebSocket serverNot supported by functionsA managed realtime service

Database connections are the classic failure. Each concurrent function instance opens its own connection, and a traffic spike exhausts max_connections in seconds:

ini
DATABASE_URL="postgres://…/db?pgbouncer=true&connection_limit=1&pool_timeout=20"
DIRECT_URL="postgres://…/db"     # migrations bypass the pooler

Use a transaction-mode pooler (PgBouncer, Supavisor, Neon or Prisma Accelerate), instantiate the client once at module scope, and never inside a handler. → Database/prisma


#Node runtime or Edge runtime

NodeEdge
APIsFull Node standard libraryWeb APIs only
Cold startHigherVery low
LocationRegion-pinnedDistributed
Database driversWorkMostly do not (no TCP)
Size limitLargerSmall bundle required
ts
export const runtime = "nodejs";     // declare it; do not rely on the default

Edge suits middleware, redirects, geolocation, header rewriting and auth checks. Anything using a database driver, a Node built-in, or a large dependency belongs on Node.

Place functions in the region nearest the database. An edge function in Sydney querying a database in Frankfurt pays that round trip on every query — edge compute helps only when the data is also close.


#Caching and revalidation

  • Static and ISR pages are served from the edge; dynamic ones execute per request. Read the next build output and confirm each route's mode is what you intended. A personalised route rendered statically is a data-leak bug, not a performance note.
  • Use revalidateTag/revalidatePath after mutations; stale content after a successful write is what users report as "it didn't save".
  • Set Cache-Control: no-store on authenticated responses. A cached authenticated response served to another visitor is the worst failure mode here.
  • Static assets are content-hashed and cached immutably by the platform — do not fight it with custom headers.

#Previews, protection and cost

  • Every pull request gets a preview deployment. Treat them as publicly reachable unless protected: enable Deployment Protection (Vercel Authentication or a password) for anything containing real data.
  • Give previews their own database, or a seeded branch database. Pointing previews at production means an untested migration runs against real data.
  • Deployments are immutable, so rollback is instant: promote a previous deployment rather than redeploying. → DevOps/rollback
  • Cost drivers, in order: function invocation and duration, edge middleware running on every request including assets, image optimisation on uncontrolled sources, and bandwidth.
    • Scope middleware.ts with a tight matcher — an unscoped middleware runs on every asset request and is frequently the largest single line item.
    • Restrict next/image remotePatterns to domains you control; an open configuration is an open image-optimisation proxy.
    • Set spend limits and usage alerts.
ts
// middleware.ts — an unscoped matcher runs on every asset request.
// This one runs on pages only, and is frequently a 10× cost difference.
export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\.(?:png|jpg|svg|woff2)$).*)"],
};

#Anti-patterns

Anti-patternWhy it failsFix
Secret in NEXT_PUBLIC_*Inlined into client JavaScriptServer-only variable
Expecting a public variable change to applyBaked at build timeRedeploy
Preview using production credentialsAny pull request writes to productionPer-environment variables
Database client per requestConnection exhaustion under loadModule-scope client + pooler
No connection poolermax_connections hit in secondsTransaction-mode pooler
Migrations through the poolerFails or corruptsDIRECT_URL
In-memory cache or rate limiterPer-instance; resets constantlyShared store
Work after the response returnsInstance frozen; work lostwaitUntil or a queue
Long-running request handlerExecution limit exceededBackground job
Writing to the filesystemRead-only and ephemeralObject storage
Edge runtime with a database driverNo TCP supportNode runtime
Functions far from the databaseRound-trip cost per queryCo-locate regions
Runtime not declaredDefaults change; surprisesDeclare it explicitly
Personalised route rendered staticallyOne user's data served to allCheck the build output
Caching authenticated responsesCross-user data exposureno-store
Unscoped middleware matcherRuns on every asset; dominates costTight matcher
Open remotePatternsFree image proxy for anyoneRestrict to your domains
Unprotected previews with real dataPublicly reachableDeployment Protection
No spend limitsCost discovered on the invoiceLimits and alerts

#Checklist

  • Verify: No secret is exposed through a NEXT_PUBLIC_ variable
  • Verify: Build-time inlining of public variables is understood; changes trigger redeploys
  • Verify: Environment variables are scoped per environment
  • Verify: Preview deployments use non-production credentials and data
  • Verify: Environment variables are validated at startup
  • Verify: One module-scope database client behind a transaction-mode pooler
  • Verify: Migrations use a direct, unpooled connection
  • Verify: No in-memory cache, rate limiter or session state is relied upon
  • Verify: Post-response work uses waitUntil or a queue
  • Verify: Long-running work runs as a background job
  • Verify: Nothing writes to the filesystem outside /tmp
  • Verify: Runtime (nodejs/edge) is declared per route
  • Verify: Functions are co-located with the database region
  • Verify: next build output is reviewed; no personalised route is static
  • Verify: Authenticated responses set no-store
  • Verify: Mutations trigger revalidation
  • Verify: middleware.ts has a tight matcher
  • Verify: next/image remotePatterns are restricted to controlled domains
  • Verify: Preview deployments are protected
  • Verify: Spend limits and usage alerts are configured