Test Your Production Build Locally Or Get Fucked

Software Development Process

Seriously, test your production build locally first. I learned this the hard way after taking down staging for two hours because I was lazy and skipped this step. The team lead was... let's just say he wasn't thrilled about explaining to the CEO why our demo was broken.

When your build breaks in production: Vercel bills you for the failed attempts (yeah, they charge for failures too), your teammates start giving you the stink eye, and instead of shipping features you're debugging webpack nonsense at 11pm on a Friday.

The "It Builds Locally" Reality Check

Run this shit exactly as written:

## Delete node_modules and package-lock.json first (trust me on this)
rm -rf node_modules package-lock.json
npm install

## Build like production actually will
NODE_ENV=production npm run build
npm start

Hit localhost:3000 and click through your app like a pissed-off user would. Open the browser console - see any red errors? Check your terminal - any warnings about chunking or dynamic imports?

Critical: If you see Warning: Failed to load resource, your images or API routes are fucked. Fix this shit now or you'll be debugging it at 2am when it breaks in production and your boss is blowing up your phone asking why the site is down.

Here's what I actually check before deploying (learned from production incidents):

  • Dynamic imports actually work - test the pages that use lazy loading
  • API routes don't timeout - hit your slowest endpoint a few times
  • Images load from the right paths - Next.js Image optimization breaks differently in prod
  • Database connections close properly - check your connection pool limits
  • Environment variables exist - your app will fail silently if they're missing

Environment Variables: Where Dreams Go to Die

Environment variables cause 90% of deployment failures. I'm not exaggerating. Vercel's interface for managing them is confusing as hell, and their scoping system will bite you in the ass when you least expect it.

The nuclear option (do this first or suffer):

## Create .env.example with FAKE values
DATABASE_URL=\"postgresql://user:pass@localhost:5432/myapp_dev\"
NEXTAUTH_SECRET=\"your-secret-here\"
STRIPE_SECRET_KEY=\"sk_test_...\"
NEXT_PUBLIC_APP_URL=\"http://localhost:3000\"

## Then create .env.local with REAL values
## NEVER FUCKING COMMIT .env.local TO GIT (yes, I've done this)

Production environment gotchas I've learned the hard way:

  1. Vercel has 3 environment scopes: Development, Preview, Production. If your Preview works but Production doesn't, you probably set variables in the wrong scope.

  2. NEXT_PUBLIC_ variables are bundled into your client code. Don't put secrets in them.

  3. Database URLs break differently in serverless - connection pooling that works locally will crash in production. Use PlanetScale or Supabase with connection pooling enabled.

  4. Vercel strips trailing slashes from URLs - if your API expects them, add redirects in next.config.js.

Dev mode clusterfuck: Sometimes Next.js just decides environment variables shouldn't reload in dev mode. You'll change a var, wonder why nothing works, then waste 20 minutes pulling your hair out before remembering to restart the damn dev server. Happens across multiple versions - ask me how I know.

Next.js 15 Config: The Stuff That Actually Matters

The Next.js docs show you pristine config files. Here's what actually works in production after I've debugged the failures:

// next.config.mjs - This actually works
/** @type {import('next').NextConfig} */
const nextConfig = {
  // This breaks in Docker if you use it wrong
  output: 'standalone',
  
  // Images will cause issues if not configured properly
  images: {
    formats: ['image/webp', 'image/avif'],
    dangerouslyAllowSVG: false, // Keep this false for security
    domains: ['cdn.yourdomain.com'], // Add your image domains here
    deviceSizes: [640, 750, 828, 1080, 1200], // Don't generate unused sizes
  },
  
  // Vercel enables this automatically, but good to be explicit
  compress: true,
  
  // This actually helps with bundle size (sometimes)
  experimental: {
    optimizePackageImports: ['@mui/material', 'lodash', 'date-fns'],
    serverComponentsExternalPackages: ['mongoose'], // If you use Mongoose
  },
  
  // Add this or your builds will randomly fail
  typescript: {
    ignoreBuildErrors: false, // Set to true if you're desperate
  },
  
  // ESLint can break builds in CI/CD
  eslint: {
    ignoreDuringBuilds: false, // Set to true if you hate yourself
  },
};

export default nextConfig;

Issues that will break your builds:

  • Missing image domains - Next.js Image optimization needs explicit domain allowlists
  • Mongoose/MongoDB connections - Add to serverComponentsExternalPackages or it'll crash
  • TypeScript errors - Fix them locally, don't ignore them in production
  • ESLint config - Your local rules might be different from Vercel's environment

Monitoring: Because Your Users Will Find the Bugs First

// app/layout.tsx - Add this or you'll deploy blind
import { SpeedInsights } from '@vercel/speed-insights/next';
import { Analytics } from '@vercel/analytics/next';

export default function RootLayout({ children }) {
  return (
    <html lang=\"en\">
      <body>
        {children}
        <SpeedInsights />
        <Analytics />
      </body>
    </html>
  );
}

Reality check: Vercel's analytics are decent but not free after your first 100k pageviews. They'll bill you. I recommend Plausible or PostHog for honest analytics without the Google privacy nightmare.

Error monitoring that actually works: Add Sentry or you'll be debugging production errors from angry user emails:

npm install @sentry/nextjs
npx @sentry/wizard -i nextjs

Database Connection Issues

Serverless + Database = Complexity. Traditional connection pooling doesn't work because each Vercel function is a separate container. Here's what actually works:

If you use Prisma (most common):

{
  \"scripts\": {
    \"db:migrate\": \"prisma migrate deploy\",
    \"db:generate\": \"prisma generate\", 
    \"build\": \"prisma generate && prisma migrate deploy && next build\",
    \"postinstall\": \"prisma generate\"
  }
}

Connection pooling for PostgreSQL: Use PgBouncer or switch to Neon, PlanetScale, or Supabase. Regular Postgres will exhaust connections and crash your functions.

MongoDB users: Use connection pooling or your app will randomly fail with MongoNetworkError. I spent an entire weekend debugging this because the error only happened under load - worked fine with one user, died with five.

Third-Party Services: The Expensive Surprise

Payment processing gotchas:

  • Stripe webhook URLs need to be updated to your production domain
  • Don't forget to switch from test keys to live keys (I've done this more than once)
  • Webhook endpoints need to be HTTPS in production

Email services that actually work in serverless:

  • Resend - built for this
  • SendGrid - reliable but expensive
  • Postmark - good deliverability
  • Don't use SMTP - it times out in serverless functions

File upload pain: Vercel functions have a 6MB request limit. Use Upstash or upload directly to S3/Cloudinary from the client.

Security Basics

Skip Content Security Policy unless you actually understand it. Most tutorials get it wrong and break your app. Here's what actually matters:

Security headers that won't break your app:

// next.config.mjs
const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          { key: 'X-Frame-Options', value: 'DENY' },
          { key: 'X-Content-Type-Options', value: 'nosniff' },
          { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
          // Don't add CSP here unless you know what you're doing
        ],
      },
    ];
  },
};

Actually important security stuff:

  • Don't commit secrets to git - use .env.local and add it to .gitignore
  • Validate API inputs - use Zod or your endpoints will be exploited
  • Rate limit your API routes - use Upstash Redis for simple rate limiting
  • Use HTTPS everywhere - Vercel handles this but check your external API calls

Pre-Flight Checklist (Do This or Suffer)

Manual testing that catches 90% of issues:

  1. Run the production build locally - npm run build && npm start
  2. Check all your forms - do they actually submit?
  3. Test authentication flows - login/logout/signup
  4. Click every navigation link - are there any 404s?
  5. Open browser dev tools - any console errors?
  6. Test on mobile - responsive design breaks in weird ways
  7. Check your API routes with curl - do they return what you expect?
## Test your API endpoints locally first (replace <your-app-name> with your actual domain)
curl -X POST https://<your-app-name>.vercel.app/api/auth/signin \
  -H \"Content-Type: application/json\" \
  -d '{\"email\":\"test@test.com\",\"password\":\"test\"}'

Bundle analysis (because Vercel will charge you):

## Check your bundle sizes
npm run build -- --analyze
open .next/analyze/client.html

## If any chunk is over 200kb, your users will hate you

Issues that often surface after deployment:

  • Images that work locally but fail in production (wrong domains)
  • API routes that timeout because you didn't test them with real data
  • Authentication redirects that break because of environment URLs
  • Database connections that worked with 1 user, fail with 10

I've learned to test everything thoroughly before deploying because debugging production issues at midnight is not fun. Trust the checklist.

Where to Actually Deploy Your Next.js App (Brutally Honest Review)

Feature

Vercel

Netlify

AWS Amplify

Self-Hosted

Railway

Actually Free

Sort of (100GB)

Sort of (100GB)

Not really

Your time

$5/month

Real Monthly Cost

$200+ with traffic

$150+

$300+ complexity

$50+ VPS

$50+

Setup Pain Level

Pretty easy

Pretty easy

AWS complexity

Challenging

Okay

Next.js Support

Excellent (they made it)

Works well

Can be tricky

DIY everything

Works fine

When It Breaks

Good support

Decent support

AWS documentation

You're on your own

Small team

Performance

Fast

Good

Can be fast

Depends on setup

Decent

Vendor Lock-in

High

Medium

Very high

None

Medium

Debugging

Pretty good

Can be tricky

AWS complexity

You control it

Basic logs

How to Deploy (And Why It's Going to Break Anyway)

Vercel Dashboard

Two ways to deploy: the "easy" dashboard way (breaks in mysterious ways) or CLI (breaks in more predictable ways). I'll show you both and the exact ways they'll screw you over because they definitely will.

Method 1: Dashboard Deploy (For People Who Like UI)

Step 1: Get Your Code Repository Ready

Push your code to GitHub/GitLab/Bitbucket. If you haven't done this already, start here.

## If you don't have git initialized yet
git init
git add .
git commit -m \"Initial commit before deployment\"

## Push to your remote (replace <username> and <repo-name> with your actual values)
git remote add origin https://github.com/<username>/<repo-name>.git
git push -u origin main

Gotcha: Make sure your package.json has the right build script configuration for Vercel. Vercel looks for npm run build by default. If yours is different, you'll need to configure it later according to the Vercel build configuration docs.

Step 2: The Vercel Song and Dance

  1. Go to vercel.com and click "Sign up" (use GitHub OAuth, it's easier)
  2. Click "New Project" (big blue button, hard to miss)
  3. Import your repository (it should show up in the list)
  4. Vercel pretends to auto-detect everything using their framework detection (it usually gets it right)

Step 3: Project Settings (Don't Touch Unless Broken)

Vercel's defaults usually work, but here's what actually matters:

  • Framework Preset: Should be "Next.js" (if not, check your folder structure)
  • Root Directory: ./ unless your Next.js app is in a subfolder (monorepo setup)
  • Build Command: npm run build (stick with the default)
  • Output Directory: .next (don't change this unless necessary)
  • Install Command: npm install (or pnpm install if you're using pnpm)

Common issues:

Step 4: Environment Variables Hell

Environment Variables Configuration

Here's where your deployment goes to die. Vercel's environment variables UI looks simple but it's a trap. They have three scopes (Development, Preview, Production) and the UI doesn't make it clear which one you're setting. I guarantee you'll fuck this up on your first deployment.

Add these in the Vercel dashboard (Project Settings → Environment Variables):

## Database (make sure it's production, not your local one)
DATABASE_URL=postgresql://user:pass@production-db.com:5432/myapp

## Authentication (generate new secret, don't reuse local one)
NEXTAUTH_SECRET=supersecretproductionstring
NEXTAUTH_URL=https://yourapp.vercel.app

## Payment processing (LIVE keys, not test keys)
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...

## Email service
RESEND_API_KEY=re_...

## Public vars (these get bundled into client code)
NEXT_PUBLIC_APP_URL=https://yourapp.vercel.app
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...

Understanding Vercel's environment scopes:

  • Production: Used for production deployments (yourapp.com)
  • Preview: Used for branch deployments (yourapp-git-feature.vercel.app)
  • Development: Used when running vercel dev locally

Common mistakes:

  • Adding secrets to Preview but not Production (or vice versa)
  • Using test API keys in production (Stripe will be angry)
  • Forgetting to update NEXTAUTH_URL to your production domain
  • Not setting the same variable in all scopes (if needed)

Step 5: Deploy and Pray

Click the big "Deploy" button and hold your breath. If you're lucky, it takes 2-3 minutes. If you're unlucky (like me about 60% of the time), you'll get cryptic build errors that would make a PhD in computer science cry.

What Vercel does when you deploy:

  1. Installs dependencies - if this fails, your package.json is fucked
  2. Runs npm run build - if this fails, your code is fucked
  3. Generates serverless functions - if this fails, your API routes are fucked
  4. Uploads to edge network - if this fails, Vercel is fucked (rarely their fault)
  5. Gives you a URL - something like your-app-abc123.vercel.app

Common build failures I've debugged:

"Cannot resolve module '../components/Header'" - Your imports are wrong. Check your relative paths and make sure you didn't accidentally import something that only exists locally. Usually it's a case sensitivity issue - Linux is pickier than macOS.

"Out of memory" - Your build is too big. Add this to package.json:

{
  \"scripts\": {
    \"build\": \"node --max-old-space-size=4096 ./node_modules/.bin/next build\"
  }
}

"Function execution timed out" - Your serverless function is doing too much. Break it down or increase the timeout in vercel.json.

"Cannot connect to database" - Your DATABASE_URL might be wrong, or your database doesn't allow connections from Vercel's IP range.

Method 2: CLI Deploy (For Terminal Users)

The CLI is more reliable for complex projects, but it has its own quirks.

Step 1: Install the CLI

npm install -g vercel
## If you get permissions errors on macOS/Linux:
sudo npm install -g vercel

Gotcha: If you're using nvm, the global install might break when you switch Node versions. Install it in each Node version you use.

Step 2: Login and Deal with Prompts

## This opens your browser for OAuth
vercel login

## Navigate to your project directory
cd your-nextjs-app

## Initialize (prepare for annoying prompts)
vercel

## The CLI will ask you dumb questions:
## \"Set up and deploy?\" → Yes (obviously)
## \"Which scope?\" → Your username (unless you have teams)
## \"Link to existing project?\" → No (if this is first deploy)
## \"Project name?\" → Whatever you want
## \"Directory?\" → ./ (unless you're in a monorepo)

CLI gotchas:

  • Vercel stores config in .vercel/ - commit this folder to git
  • First run creates the project - subsequent runs just deploy
  • CLI auth tokens expire - you'll randomly get logged out and need to re-auth

Step 3: vercel.json Config (Advanced Configuration)

Most projects don't need a vercel.json, but if you need custom settings, here's what actually works:

{
  \"framework\": \"nextjs\",
  \"regions\": [\"iad1\", \"sfo1\"], // Only deploy to US East/West (faster builds)
  \"functions\": {
    \"app/api/**/*.ts\": {
      \"maxDuration\": 30 // Increase timeout for slow API routes
    }
  },
  \"headers\": [
    {
      \"source\": \"/api/(.*)\",
      \"headers\": [
        {
          \"key\": \"Cache-Control\", 
          \"value\": \"s-maxage=60, stale-while-revalidate\"
        }
      ]
    }
  ],
  \"redirects\": [
    {
      \"source\": \"/old-url\",
      \"destination\": \"/new-url\", 
      \"permanent\": true
    }
  ]
}

When you actually need vercel.json:

  • Custom function timeouts - default is 10 seconds, some APIs need more
  • Regional deployment - if you only serve specific regions
  • Custom headers - for caching or security headers
  • Redirects - for URL changes or SEO

Don't add vercel.json unless you need it - Vercel's defaults work for 90% of projects.

Step 4: Environment Variables via CLI

Setting env vars through CLI is tedious but sometimes necessary:

## Add variables one by one
vercel env add DATABASE_URL production
## It will prompt you to enter the value

## For each environment (production, preview, development)
vercel env add NEXTAUTH_SECRET production
vercel env add NEXTAUTH_SECRET preview  
vercel env add NEXTAUTH_SECRET development

## Pull production env vars to local file  
vercel env pull .env.local

CLI environment variable pain points:

  • No bulk import - you have to add them one by one
  • No easy editing - to change a value, you remove and re-add it
  • Easy to forget scopes - forgetting to add to all environments

Pro tip: Use the dashboard for setting env vars. The CLI is only good for pulling them locally.

Step 5: Deploy Commands That Actually Work

## Deploy to production (do this after testing)
vercel --prod

## Deploy and follow logs (useful for debugging)
vercel --prod --debug

## Deploy specific branch to production  
git checkout main
vercel --prod

## Force rebuild (when cache is causing issues)
vercel --prod --force

Common CLI deployment issues:

  • "No existing deployments" - run vercel first (without --prod) to create project
  • "Invalid token" - your auth expired, run vercel login again
  • "Build failed" - same issues as dashboard deploy, but better error messages
  • "Deployment timed out" - your build is too slow, optimize or use CI/CD

When Your Deployment Inevitably Breaks

Custom Build Scripts (For Complex Projects)

If your app needs database migrations or code generation:

{
  \"scripts\": {
    \"build\": \"prisma generate && next build\",
    \"vercel-build\": \"prisma generate && prisma migrate deploy && next build\"
  }
}

Why vercel-build instead of build? Vercel runs vercel-build if it exists, build otherwise. Use this for production-specific build steps.

Build script gotchas:

Environment Strategy That Actually Works

## Preview deployments (branch deployments)
vercel                    # Deploys current branch

## Production deployment  
vercel --prod            # Only deploys from main/master branch

Branch deployment workflow I actually use:

  1. Feature branches get preview deployments (yourapp-git-feature.vercel.app)
  2. Main branch gets production deployment (yourapp.vercel.app)
  3. Preview before production - test the preview URL before merging

Database Migration Reality Check

Don't run migrations during build unless you enjoy 3 AM incidents.

Safer approach:

{
  \"scripts\": {
    \"build\": \"prisma generate && next build\",
    \"migrate:prod\": \"prisma migrate deploy\"
  }
}

Run migrations separately:

## Deploy code first
vercel --prod

## Then run migrations (after confirming deployment works)
vercel env pull .env.local
npm run migrate:prod

Migration horror stories I've personally caused:

  • Breaking schema change during deployment - app went down for 2 hours, customers couldn't checkout
  • Migration failed mid-deployment - database in inconsistent state, had to manually fix 10k records
  • Rollback impossible - had to fix migration forward instead of reverting, worked until 3am

Rule: Test migrations in preview environment with production-like data first.

CI/CD: Do You Actually Need It?

Hot take: Most small projects don't need CI/CD. Vercel's GitHub integration already does continuous deployment. Adding GitHub Actions is often overkill.

When you actually need CI/CD:

Simple GitHub Actions that actually helps:

name: Test before deploy
on: [pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - run: npm run test

This runs tests on PRs but lets Vercel handle actual deployment. Simple and effective.

Custom Domains: The DNS Nightmare

DNS Configuration

Adding a custom domain in Vercel:

  1. Go to Project Settings → Domains
  2. Add your domain (e.g., yourapp.com)
  3. Configure DNS records (this is where it gets painful)

DNS settings that actually work:

## For root domain (yourapp.com)
A     @     76.76.19.61

## For www subdomain  
CNAME www   cname.vercel-dns.com

Common DNS issues:

  • Wrong A record IP - Vercel's IP changed, check their docs
  • Missing CNAME - www subdomain won't work without proper CNAME record
  • DNS caching - changes take 24-48 hours to propagate globally
  • Cloudflare proxy - turn off the orange cloud if using Vercel

Domain verification fails?

  • Check DNS with dig yoursite.com
  • Wait longer (DNS is slow as molasses)
  • Try incognito mode to bypass local DNS cache

After Deployment: The Real Work Begins

Your app is live. Now what breaks?

  1. Check the actual URL - does your site load?
  2. Test all functionality - forms, auth, API routes
  3. Check browser console - any errors?
  4. Monitor Vercel logs - Functions tab shows API errors
  5. Set up error tracking - Sentry will save your ass

Production debugging tools:

## Check deployment status
vercel logs yourapp.vercel.app

## Follow real-time logs  
vercel logs yourapp.vercel.app --follow

## Check specific function logs
vercel logs yourapp.vercel.app --since=1h

Real production issues I've personally debugged:

  • Images showing 403 Forbidden - wrong Next.js Image domains config, forgot to add CDN domain
  • API routes timing out after exactly 10 seconds - database connection pool exhausted, hit Vercel's function limit
  • Authentication broken after deployment - NEXTAUTH_URL still set to localhost instead of production domain (classic mistake)
  • CORS errors on preflight requests - API routes not handling OPTIONS requests properly
  • "MongoNetworkError: connection timed out" - serverless cold starts with traditional connection pooling

The deployment is just the beginning. Production debugging at 3 AM is where you really learn.

When Your Deployment Inevitably Breaks (Debugging 101)

Q

"Module not found" - Classic import fuckery

A

Your imports work locally but Vercel's build shits the bed.

This happens because your local machine (probably macOS) is more forgiving than Vercel's Linux build environment. Here's the usual suspects:Case sensitivity causing problems:```javascript// ❌ Your file is myComponent.tsx but you import:import MyComponent from './MyComponent';// Error:

Module not found: Can't resolve './MyComponent'// ✅ Match the actual filename exactly:import My

Component from './myComponent';```The dreaded macOS vs Linux case problem

  • macOS doesn't care about case, Linux (Vercel's build environment) does.

Check every single import.Relative path hell:javascript// ❌ This works locally because of how your bundler resolves:import utils from '../../utils/helpers';// ✅ But the file is actually helpers.ts:import utils from '../../utils/helpers.ts';// Or use absolute imports:import utils from '@/utils/helpers';Set up absolute imports or suffer forever:```javascript// tsconfig.json

  • do this early or regret it later{ "compilerOptions": { "baseUrl": ".", "paths": { "@/": ["./src/"], "@/components/": ["./components/"] } }}```
Q

"Build timed out" - When your build takes too long

A

Your build is taking too long because something's consuming too much time during the build process.

Here's what's usually happening:Common build time killers:

  • API calls during build (don't do this, ever)
  • Massive bundle sizes (large libraries you're not fully using)
  • Too many pages generating at build time
  • Database connections during SSG (this slows things down significantly)Quick fixes that actually work:bash# Check what's taking forever during buildNODE_OPTIONS="--max-old-space-size=4096" npm run build# Find your biggest dependenciesnpm install -g bundle-analyzernpx bundle-analyzer .next/static/js/*.jsLazy load heavy shit:javascript// ❌ Importing massive charting library everywhere:import Chart from 'react-chartjs-2';// ✅ Only load when needed:const Chart = dynamic(() => import('react-chartjs-2'), { ssr: false, loading: () => <div>Loading chart...</div>});If you have 1000+ pages, use ISR instead of SSG:javascript// Don't generate everything at build timeexport async function getStaticProps() { return { props: { data }, revalidate: 3600 // Generate on-demand, cache for 1 hour };}
Q

Environment variables broken in production (the classic nightmare)

A

I've probably wasted 50+ hours of my life debugging env var issues.

Here's what's usually wrong (though sometimes it's something even stupider that'll make you question your career choices):You're trying to access server variables on the client side:```javascript// ❌ This returns undefined on the client:const secret = process.env.

DATABASE_URL; // Only available on server// ✅ If you need it on client, prefix with NEXT_PUBLIC_:const apiUrl = process.env.

NEXT_PUBLIC_API_URL;```Vercel's environment scope system is confusing as hell:

  • Production:

Only used for production deployments (yourapp.com)

  • Preview: Used for branch/PR deployments (yourapp-git-branch.vercel.app)
  • Development:

Used for vercel dev locally (rarely needed)Common mistake: You set the var in Preview but not Production, or vice versa.

Usually need to set them in both.The rebuild gotcha: After adding env vars, you MUST redeploy.

They don't magically appear.Case sensitivity matters:```bash# ❌ These are different variables:

DATABASE_URLdatabase_urlDatabase_UrlDebug env vars in production:javascript// Add this to your API route to see what env vars exist:console.log('All env vars:', Object.keys(process.env));// Check the Vercel function logs to see the output```

Q

"My app was fast locally, now it's slow as shit in production"

A

Welcome to the wonderful world of serverless cold starts, database connection limits, and the harsh reality that "localhost" doesn't have network latency.

Here's what's probably killing your performance:Serverless cold starts (the #1 killer):

  • Your function hasn't run in 5+ minutes → cold start penalty (300-500ms)
  • First user after downtime gets the slow response
  • Solution:

Keep functions warm with cron jobs or accept the realityDatabase connection pool exhaustion:```javascript// ❌ This creates new connection every time (disaster in serverless):const db = new Pool({ connectionString: process.env.

DATABASE_URL });// ✅ Use connection pooling service or singleton pattern:import { db } from '@/lib/db-singleton';// And limit concurrent connections in your database serviceThe N+1 query problem that kills performance:javascript// ❌ This makes 100 database queries for 100 posts:const posts = await db.post.findMany();for (const post of posts) { post.author = await db.user.findUnique({ where: { id: post.authorId } });}// ✅ One query with joins:const posts = await db.post.findMany({ include: { author: true } });Images not optimized (Next.js Image component gotchas):javascript// ❌ This bypasses Next.js optimization:Slow loading// ✅ But Next.js Image has its own gotchas:import Image from 'next/image';<Image src="/big-image.jpg" alt="Optimized" width={800} height={600} priority // For above-the-fold images placeholder="blur" // Requires blurDataURL or static import/>Bundle size explosion:bash# See what's actually in your bundle:npm run build -- --analyze# Look for the massive chunks and dynamic import them```

Q

"Function execution timed out" (The 10-second death sentence)

A

Your API route is taking too long and Vercel is killing it. Free tier gives you 10 seconds, Pro gives you 60 seconds. Here's how to not hit the timeout:Don't do heavy processing in API routes:javascript// ❌ This will timeout with any real data:export async function GET() { const users = await db.user.findMany(); // 10k users const processedData = users.map(user => heavyProcessing(user)); // Takes 30 seconds return Response.json(processedData);}// ✅ Move heavy processing to background jobs:export async function GET() { // Trigger background job and return immediately await queue.enqueue('processUsers', { userId: user.id }); return Response.json({ message: 'Processing started' });}Database queries that take forever:javascript// ❌ Complex queries without indexes:const results = await db.post.findMany({ where: { content: { contains: searchTerm }, // No index = timeout author: { followers: { gt: 1000 } } }, include: { author: true, comments: { include: { author: true } } }});// ✅ Optimize or cache:const results = await redis.get(cacheKey) || await db.post.findMany({ /* optimized query */ });If you must do heavy processing, use streaming:```javascriptexport async function GET() { const stream = new ReadableStream({ start(controller) { processInChunks(data, (chunk) => { controller.enqueue(`data: ${JSON.stringify(chunk)}

`); }); } }); return new Response(stream, { headers: { 'Content-Type': 'text/stream' } });}```

Q

"This site can't be reached" - Domain debugging hell

A

DNS is black magic and will absolutely ruin your day.

I once spent 6 hours debugging DNS only to realize I'd been testing the wrong fucking domain the entire time. Still makes me angry thinking about it. Here's the debugging process that actually works:Check if your DNS is fucked:bash# See what your domain actually points todig yourdomain.com Adig www.yourdomain.com CNAME# Should show:# yourdomain.com A 76.76.19.61 # www.yourdomain.com CNAME cname.vercel-dns.comCommon DNS fuckups:

  • Wrong A record IP
  • Vercel's IP changes sometimes, check their docs
  • Missing www CNAME
  • Your site works on yourdomain.com but not www.yourdomain.com
  • DNS propagation
  • Can take 24-48 hours, use different DNS servers to check
  • Cloudflare orange cloud
  • Turn off the proxy if using VercelThe "it works for me" problem:

Your ISP might cache DNS differently. Test from multiple locations:

  • Use nslookup yourdomain.com 8.8.8.8 (Google DNS)
  • Check from your phone on cellular data
  • Use online DNS checkers
Q

SSL certificate hell

A

"Your connection is not secure" means:

  • Certificate not issued yet

  • Wait 48 hours after adding domain

  • Mixed content

  • You're loading HTTP resources on HTTPS site

  • Wrong domain

  • Certificate issued for yourdomain.com but accessing www.yourdomain.comDebug SSL issues:bash# Check certificate detailsopenssl s_client -connect yourdomain.com:443 -servername yourdomain.comForce SSL refresh (sometimes works):

  • Remove domain from Vercel dashboard

  • Wait 5 minutes

  • Re-add domain

  • Pray to the SSL gods

Q

"Connection terminated unexpectedly" - The serverless challenge

A

Your database works locally but breaks in production because serverless functions don't maintain persistent connections.

Here's what usually works:Connection pool exhaustion (most common):```javascript// ❌ This creates infinite connections and crashes your DB:export async function GET() { const db = new Pool({ connectionString: process.env.

DATABASE_URL }); const result = await db.query('SELECT * FROM users'); return Response.json(result.rows);}// ✅ Use a singleton or connection pooling service:// Use Pg

Bouncer, Supabase, or PlanetScale with built-in poolingimport { pool } from '@/lib/db-singleton';SSL connection issues:javascript// Production databases usually require SSLconst pool = new Pool({ connectionString: process.env.

DATABASE_URL, ssl: process.env.

NODE_ENV === 'production' ? { rejectUnauthorized: false } : false});```Connection limits hit:

  • PostgreSQL default: 100 connections (exhausted quickly)
  • Each Vercel function:

Can create multiple connections

  • Solution: Use connection pooling service or limit concurrent functionsPrisma gotchas in serverless:javascript// prisma/schema.prismadatasource db { provider = "postgresql" url = env("DATABASE_URL") // Pooled connection directUrl = env("POSTGRES_URL_NON_POOLING") // For migrations}
Q

API routes randomly return 500 errors (The production mystery)

A

Check Vercel function logs first:```bash# See what's actually failingvercel logs --follow# Or check in the dashboard:

Project → Functions tab```Common API route failures:

  • Environment variables missing (different scopes in Vercel)
  • Database connection failures (see above)
  • Import errors (case sensitivity in production)
  • Unhandled exceptions (error that worked locally but breaks in production)Better error handling:javascript// app/api/example/route.tsexport async function GET() { try { const data = await riskyOperation(); return Response.json(data); } catch (error) { // Log the actual error for debugging console.error('API route failed:', error); // Don't expose internal errors to client return Response.json( { error: 'Something went wrong' }, { status: 500 } ); }}
Q

"My changes aren't showing up" - When caching attacks

A

Next.js caches everything aggressively.

Sometimes too aggressively:Nuclear option (when nothing else works):bash# Force redeploy without any cachingvercel --forceISR (Incremental Static Regeneration) gotchas:javascript// This page will be cached for 1 hourexport const revalidate = 3600;// But if you change it to 60 seconds, existing cache doesn't know// You need to deploy AND wait for cache to expireClient-side caching also fucks you:

  • Browser cache
  • Hard refresh (Cmd+Shift+R)
  • Vercel edge cache
  • Can take up to 60 seconds to update
  • CDN cache
  • If using Cloudflare, purge their cache tooDebug what's actually cached:```javascriptexport async function GET() { console.log('API called at:', new Date().to

ISOString()); // If you don't see new timestamps, it's cached somewhere}```

Q

The "it works locally" debugging process:

A

Check Vercel function logs (usually the first clue)2. Compare local vs production environment variables 3. Test the production build locally (npm run build && npm start)4. Check browser network tab for 500s or failed requests 5. Verify database connections and API keys are production-ready 6. Test on different devices/networks (sometimes it's just you)Still stuck? Try the Vercel Discord and post your error logs. The community is usually helpful for debugging weird issues.

Resources That Don't Suck (Mostly)

Related Tools & Recommendations

integration
Similar content

Supabase Next.js 13+ Server-Side Auth Guide: What Works & Fixes

Here's what actually works (and what will break your app)

Supabase
/integration/supabase-nextjs/server-side-auth-guide
100%
compare
Similar content

Heroku Alternatives: Vercel, Railway, Render, Fly.io Compared

Vercel, Railway, Render, and Fly.io - Which one won't bankrupt you?

Vercel
/compare/vercel/railway/render/fly/deployment-platforms-comparison
86%
compare
Recommended

Framework Wars Survivor Guide: Next.js, Nuxt, SvelteKit, Remix vs Gatsby

18 months in Gatsby hell, 6 months testing everything else - here's what actually works for enterprise teams

Next.js
/compare/nextjs/nuxt/sveltekit/remix/gatsby/enterprise-team-scaling
85%
compare
Similar content

Remix vs SvelteKit vs Next.js: SSR Performance Showdown

I got paged at 3AM by apps built with all three of these. Here's which one made me want to quit programming.

Remix
/compare/remix/sveltekit/ssr-performance-showdown
72%
tool
Similar content

Fix Astro Production Deployment Nightmares: Troubleshooting Guide

Troubleshoot Astro production deployment issues: fix 'JavaScript heap out of memory' build crashes, Vercel 404s, and server-side problems. Get platform-specific

Astro
/tool/astro/production-deployment-troubleshooting
66%
tool
Similar content

Astro Overview: Static Sites, React Integration & Astro 5.0

Explore Astro, the static site generator that solves JavaScript bloat. Learn about its benefits, React integration, and the game-changing content features in As

Astro
/tool/astro/overview
53%
compare
Similar content

Astro, Next.js, Gatsby: Static Site Generator Benchmark

Just use fucking Astro. Next.js if you actually need server shit. Gatsby is dead - seriously, stop asking.

Astro
/compare/astro/nextjs/gatsby/static-generation-performance-benchmark
53%
tool
Similar content

SvelteKit: Fast Web Apps & Why It Outperforms Alternatives

I'm tired of explaining to clients why their React checkout takes 5 seconds to load

SvelteKit
/tool/sveltekit/overview
52%
tool
Similar content

Qwik Production Deployment: Edge, Scaling & Optimization Guide

Real-world deployment strategies, scaling patterns, and the gotchas nobody tells you

Qwik
/tool/qwik/production-deployment
52%
pricing
Recommended

Backend Pricing Reality Check: Supabase vs Firebase vs AWS Amplify

Got burned by a Firebase bill that went from like $40 to $800+ after Reddit hug of death. Firebase real-time listeners leak memory if you don't unsubscribe prop

Supabase
/pricing/supabase-firebase-amplify-cost-comparison/comprehensive-pricing-breakdown
51%
tool
Similar content

Migrate from Create React App to Vite & Next.js: A Practical Guide

Stop suffering with 30-second dev server startup. Here's how to migrate to tools that don't make you want to quit programming.

Create React App
/tool/create-react-app/migration-guide
46%
integration
Similar content

Stripe Next.js Serverless Performance: Optimize & Fix Cold Starts

Cold starts are killing your payments, webhooks are timing out randomly, and your users think your checkout is broken. Here's how to fix the mess.

Stripe
/integration/stripe-nextjs-app-router/serverless-performance-optimization
46%
tool
Similar content

Next.js Overview: Features, Benefits & Next.js 15 Updates

Explore Next.js, the powerful React framework with built-in routing, SSR, and API endpoints. Understand its core benefits, when to use it, and what's new in Nex

Next.js
/tool/nextjs/overview
43%
tool
Recommended

Stripe Terminal React Native SDK - Turn Your App Into a Payment Terminal That Doesn't Suck

built on Stripe Terminal React Native SDK

Stripe Terminal React Native SDK
/tool/stripe-terminal-react-native-sdk/overview
41%
pricing
Recommended

Got Hit With a $3k Vercel Bill Last Month: Real Platform Costs

These platforms will fuck your budget when you least expect it

Vercel
/pricing/vercel-vs-netlify-vs-cloudflare-pages/complete-pricing-breakdown
39%
troubleshoot
Similar content

Fix Slow Next.js Build Times: Boost Performance & Productivity

When your 20-minute builds used to take 3 minutes and you're about to lose your mind

Next.js
/troubleshoot/nextjs-slow-build-times/build-performance-optimization
39%
tool
Similar content

Vercel Overview: Deploy Next.js Apps & Get Started Fast

Get a no-bullshit overview of Vercel for Next.js app deployment. Learn how to get started, understand costs, and avoid common pitfalls with this practical guide

Vercel
/tool/vercel/overview
39%
tool
Recommended

Supabase - PostgreSQL with Bells and Whistles

integrates with Supabase

Supabase
/tool/supabase/overview
37%
integration
Recommended

Stop Your APIs From Breaking Every Time You Touch The Database

Prisma + tRPC + TypeScript: No More "It Works In Dev" Surprises

Prisma
/integration/prisma-trpc-typescript/full-stack-architecture
37%
tool
Similar content

Webflow Production Deployment: Real Engineering & Troubleshooting Guide

Debug production issues, handle downtime, and deploy websites that actually work at scale

Webflow
/tool/webflow/production-deployment
36%

Recommendations combine user behavior, content similarity, research intelligence, and SEO optimization