How to Deploy Your SaaS Boilerplate to Production 2026
TL;DR
Vercel + Neon + Resend + Stripe (live mode) is the default production stack for Next.js SaaS in 2026. Deployment itself takes 1-2 hours. The checklist below covers the steps most developers forget — especially environment variables, database migrations, domain setup, and Stripe webhook registration.
The Production Stack
| Layer | Service | Cost |
|---|---|---|
| Hosting | Vercel Pro | $20/mo |
| Database | Neon (Serverless Postgres) | $19/mo |
| Resend | $0 → $20/mo | |
| Payments | Stripe | 2.9% + 30¢ |
| Auth | NextAuth (self-hosted) | Free |
| Error tracking | Sentry | Free tier |
| Analytics | PostHog | Free tier |
Step 1: Database (Neon)
# 1. Create production database at neon.tech
# 2. Copy connection string
# 3. Run migrations against production
DATABASE_URL="postgresql://..." npx prisma migrate deploy
# 4. Verify migrations
DATABASE_URL="postgresql://..." npx prisma db push --preview-feature
Neon branching for zero-downtime migrations:
# Before deploying a breaking migration:
# 1. Create a branch: main → migration-branch
# 2. Apply migration to branch
# 3. Deploy app pointing to branch
# 4. Verify, then promote branch to main
Step 2: Vercel Project Setup
# Install Vercel CLI
npm i -g vercel
# Link and deploy
vercel --prod
# Or connect via GitHub:
# vercel.com → New Project → Import from GitHub
Build settings for Next.js:
// vercel.json (optional — usually auto-detected)
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"installCommand": "npm ci"
}
If using Prisma, generate client during build:
// package.json
{
"scripts": {
"build": "prisma generate && next build",
"postinstall": "prisma generate"
}
}
Step 3: Environment Variables
Set in Vercel dashboard → Project Settings → Environment Variables. Select Production environment.
# Core
NEXTAUTH_URL=https://yoursaas.com
NEXTAUTH_SECRET=<generate with: openssl rand -base64 32>
# Database
DATABASE_URL=postgresql://...
# Stripe (LIVE keys, not test)
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET= # Set after step 4
# Email
RESEND_API_KEY=re_...
EMAIL_FROM=YourSaaS <noreply@yoursaas.com>
# OAuth (if using)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
# Error tracking
SENTRY_DSN=
NEXT_PUBLIC_SENTRY_DSN=
Critical: Never use test Stripe keys in production. sk_test_ keys will silently fail on real payments.
Step 4: Stripe Webhook Registration
# Register your production webhook endpoint in Stripe dashboard:
# Stripe Dashboard → Developers → Webhooks → Add endpoint
# URL: https://yoursaas.com/api/stripe/webhook
# Or via CLI:
stripe listen --forward-to https://yoursaas.com/api/stripe/webhook
Events to subscribe to (minimum):
checkout.session.completed
customer.subscription.created
customer.subscription.updated
customer.subscription.deleted
invoice.payment_succeeded
invoice.payment_failed
Verify your webhook handler checks the signature:
// app/api/stripe/webhook/route.ts
const sig = req.headers.get('stripe-signature')!;
const body = await req.text();
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return new Response('Webhook signature verification failed', { status: 400 });
}
Step 5: Custom Domain
# In Vercel dashboard → Project → Domains → Add
# Add: yoursaas.com and www.yoursaas.com
# DNS records to add at your registrar:
# A record: @ → 76.76.21.21 (Vercel IP)
# CNAME record: www → cname.vercel-dns.com
Update environment variables after domain is live:
NEXTAUTH_URL=https://yoursaas.com # Was localhost:3000
Step 6: Email Domain Verification (Resend)
# In Resend dashboard → Domains → Add Domain
# Add DNS records:
# TXT record: resend._domainkey.yoursaas.com → (DKIM key from Resend)
# TXT record: yoursaas.com → v=spf1 include:amazonses.com ~all
# Verify in Resend dashboard (takes 10-30 min to propagate)
Pre-Launch Verification Checklist
# Auth
[ ] Sign up with email works → welcome email received
[ ] Google OAuth login works
[ ] Sign out works
[ ] Password reset email received
# Payments
[ ] Checkout page loads with correct prices
[ ] Test purchase with: 4242 4242 4242 4242 (Stripe test card)
[ ] Switch to LIVE mode and verify a real $1 test charge
[ ] Webhook fires and subscription created in DB
[ ] Billing portal loads via Manage Billing button
# Core Features
[ ] Dashboard loads after login
[ ] Feature gating works (Pro features blocked for free users)
[ ] Settings page saves
# Error Handling
[ ] 404 page renders (visit /does-not-exist)
[ ] Error boundary works (no blank screens)
[ ] Sentry receives test event: Sentry.captureException(new Error("test"))
# Performance
[ ] Lighthouse score ≥ 90 on /
[ ] Core Web Vitals: LCP < 2.5s
[ ] No console errors on load
Common Deployment Failures
| Error | Cause | Fix |
|---|---|---|
NEXTAUTH_URL mismatch | Env var not updated for production domain | Set to https://yoursaas.com |
| Prisma migration errors | Using migrate dev instead of migrate deploy | Use prisma migrate deploy in build |
| OAuth redirect mismatch | Google/GitHub OAuth not updated | Add production URL to OAuth app |
| Stripe webhooks 401 | Wrong webhook secret | Regenerate in Stripe and update env var |
| Email from Resend sandbox | Domain not verified | Complete Resend DNS verification |
Compare boilerplates and their deployment complexity on StarterPick.
Check out this boilerplate
View ShipFast on StarterPick →