Vercel Deployment
This skill helps you deploy and configure Next.js applications on Vercel following best practices.
Quick Deploy Checklist
-
Environment variables set in Vercel dashboard
-
Build command configured (default: next build )
-
Output directory correct (default: .next )
-
Node.js version specified (20.x recommended)
-
Database accessible from Vercel's network
-
Secrets not committed to git
Environment Variables
Setting Variables
Vercel Dashboard (Recommended for secrets):
-
Project Settings → Environment Variables
-
Add variable with appropriate scope:
-
Production: Only production deployments
-
Preview: PR and branch previews
-
Development: Local vercel dev
Via CLI:
vercel env add VARIABLE_NAME production vercel env pull .env.local # Pull to local
Variable Naming
Server-only (never exposed to browser)
DATABASE_URL= SESSION_SECRET= ANTHROPIC_API_KEY=
Client-exposed (prefixed with NEXT_PUBLIC_)
NEXT_PUBLIC_APP_URL= NEXT_PUBLIC_ANALYTICS_ID=
Size Limits
Context Limit
Total per deployment 64 KB
Edge Functions 5 KB per variable
Single variable 64 KB max
Required Variables for This Project
Authentication (required)
SESSION_SECRET=your-32-char-minimum-secret-here
AI Integration (required for chat)
ANTHROPIC_API_KEY=sk-ant-api...
Database (if using external)
DATABASE_URL=file:./data/app.db
Push Notifications (optional)
VAPID_PUBLIC_KEY= VAPID_PRIVATE_KEY= VAPID_SUBJECT=mailto:admin@example.com
OAuth (optional)
GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= APPLE_CLIENT_ID= APPLE_CLIENT_SECRET=
vercel.json Configuration
{ "buildCommand": "npm run build", "framework": "nextjs", "regions": ["iad1"], "headers": [ { "source": "/api/(.)", "headers": [ { "key": "Cache-Control", "value": "no-store, must-revalidate" } ] }, { "source": "/(.)", "headers": [ { "key": "X-Content-Type-Options", "value": "nosniff" }, { "key": "X-Frame-Options", "value": "DENY" }, { "key": "X-XSS-Protection", "value": "1; mode=block" } ] } ], "redirects": [ { "source": "/old-path", "destination": "/new-path", "permanent": true } ], "rewrites": [ { "source": "/api/v1/:path*", "destination": "/api/:path*" } ] }
TypeScript Configuration (New in 2025)
// vercel.ts - Type-safe configuration import { defineConfig } from '@vercel/config';
export default defineConfig({ regions: ['iad1'],
headers: async () => [ { source: '/api/:path*', headers: [ { key: 'Cache-Control', value: 'no-store' }, ], }, ],
redirects: async () => [ { source: '/old', destination: '/new', permanent: true, }, ], });
Edge Functions
When to Use Edge
Good for:
-
Authentication/authorization
-
A/B testing
-
Geolocation-based content
-
Request/response transforms
-
Simple, fast operations
Not suitable for:
-
Database connections (use serverless instead)
-
Long-running operations
-
Large dependencies
Edge Function Example
// src/middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server';
export const config = { matcher: ['/api/:path*', '/protected/:path*'], };
export function middleware(request: NextRequest) { // Check auth token const token = request.cookies.get('session')?.value;
if (!token && request.nextUrl.pathname.startsWith('/protected')) { return NextResponse.redirect(new URL('/login', request.url)); }
// Add headers const response = NextResponse.next(); response.headers.set('X-Request-Id', crypto.randomUUID());
return response; }
Edge Runtime in API Routes
// src/app/api/edge-example/route.ts export const runtime = 'edge';
export async function GET(request: Request) { // Limited to edge-compatible APIs return Response.json({ timestamp: Date.now() }); }
Build Configuration
package.json Scripts
{ "scripts": { "build": "next build", "postbuild": "npm run db:generate" } }
Build Environment
Set Node.js version
In Vercel Dashboard → Settings → General → Node.js Version
Or in package.json:
{ "engines": { "node": "20.x" } }
Build Output
Check build locally
npm run build
Analyze bundle
ANALYZE=true npm run build
Database Considerations
SQLite on Vercel
SQLite with better-sqlite3 works in Vercel's serverless functions, but:
-
Filesystem is read-only except /tmp
-
Data doesn't persist between invocations
-
Not suitable for production data storage
Production Database Options
Turso (SQLite edge database)
import { createClient } from '@libsql/client';
const db = createClient({ url: process.env.TURSO_DATABASE_URL!, authToken: process.env.TURSO_AUTH_TOKEN, });
Vercel Postgres
import { sql } from '@vercel/postgres';
const result = await sqlSELECT * FROM users;
PlanetScale (MySQL)
Neon (Postgres)
Preview Deployments
Branch Previews
Every git push creates a preview deployment:
-
https://<project>-<branch>-<team>.vercel.app
-
Separate environment variables for preview
Preview Environment Variables
Different values for preview vs production
In Vercel Dashboard, set both:
DATABASE_URL (Production): postgres://prod-db... DATABASE_URL (Preview): postgres://staging-db...
Commenting on PRs
Vercel automatically comments on PRs with:
-
Preview URL
-
Build status
-
Performance metrics
Troubleshooting
Build Failures
Check build locally first
npm run build
Common issues:
- Missing environment variables
- TypeScript errors
- ESLint errors (strict mode)
- Missing dependencies
Environment Variable Issues
Verify variables are set
vercel env ls
Pull to local for debugging
vercel env pull .env.local
Function Timeout
// Increase timeout (max 60s on Pro, 10s on Hobby) // In vercel.json: { "functions": { "api/long-running.ts": { "maxDuration": 60 } } }
Memory Issues
// Increase memory (affects cost) { "functions": { "api/heavy-processing.ts": { "memory": 1024 } } }
Monitoring
Vercel Analytics
// src/app/layout.tsx import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }) { return ( <html> <body> {children} <Analytics /> </body> </html> ); }
Speed Insights
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }) { return ( <html> <body> {children} <SpeedInsights /> </body> </html> ); }
Function Logs
View logs via CLI
vercel logs <deployment-url>
Real-time logs
vercel logs <deployment-url> --follow
Domains
Custom Domain Setup
-
Vercel Dashboard → Domains
-
Add domain
-
Configure DNS:
-
A record: 76.76.21.21
-
Or CNAME: cname.vercel-dns.com
-
SSL automatically provisioned
Redirects
// vercel.json { "redirects": [ { "source": "/:path((?!api/).*)", "has": [{ "type": "host", "value": "old-domain.com" }], "destination": "https://new-domain.com/:path", "permanent": true } ] }
Security
Protected Routes
Use middleware for authentication checks (see Edge Functions above).
Rate Limiting
Implement application-level rate limiting since Vercel doesn't provide built-in rate limiting for serverless functions.
Secrets Management
-
Never commit .env files
-
Use Vercel's encrypted environment variables
-
Rotate secrets regularly
-
Different secrets for preview vs production
References
-
Vercel Documentation
-
Environment Variables
-
Edge Functions
-
Next.js on Vercel