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:
bash
vercel env add VARIABLE_NAME production vercel env pull .env.local # Pull to local
Variable Naming
bash
# 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
bash
# 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
json
{
"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)
typescript
// 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
typescript
// 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
typescript
// 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
json
{
"scripts": {
"build": "next build",
"postbuild": "npm run db:generate"
}
}
Build Environment
bash
# Set Node.js version
# In Vercel Dashboard → Settings → General → Node.js Version
# Or in package.json:
{
"engines": {
"node": "20.x"
}
}
Build Output
bash
# 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)
typescriptimport { createClient } from '@libsql/client'; const db = createClient({ url: process.env.TURSO_DATABASE_URL!, authToken: process.env.TURSO_AUTH_TOKEN, }); - •
Vercel Postgres
typescriptimport { sql } from '@vercel/postgres'; const result = await sql`SELECT * 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
bash
# 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
bash
# Check build locally first npm run build # Common issues: # - Missing environment variables # - TypeScript errors # - ESLint errors (strict mode) # - Missing dependencies
Environment Variable Issues
bash
# Verify variables are set vercel env ls # Pull to local for debugging vercel env pull .env.local
Function Timeout
typescript
// Increase timeout (max 60s on Pro, 10s on Hobby)
// In vercel.json:
{
"functions": {
"api/long-running.ts": {
"maxDuration": 60
}
}
}
Memory Issues
typescript
// Increase memory (affects cost)
{
"functions": {
"api/heavy-processing.ts": {
"memory": 1024
}
}
}
Monitoring
Vercel Analytics
typescript
// src/app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
);
}
Speed Insights
typescript
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<SpeedInsights />
</body>
</html>
);
}
Function Logs
bash
# 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
- •A record:
- •SSL automatically provisioned
Redirects
json
// 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
.envfiles - •Use Vercel's encrypted environment variables
- •Rotate secrets regularly
- •Different secrets for preview vs production