Deploying Your NextJS AI App: The Complete Production Checklist
Avoid costly mistakes when deploying your NextJS AI app to production. Complete guide covering Vercel, Supabase, Stripe webhooks, and monitoring setup.
Most developers can get a NextJS app running locally in a few hours. But deploying to production - especially an AI app with vector databases, API integrations, and payment processing - is where projects stall for weeks.
AI apps have unique deployment challenges. You're not just hosting static pages. You're managing API keys for multiple services, vector databases with embeddings, streaming responses, webhook endpoints, and potentially expensive API costs that can spiral out of control without proper rate limiting.
This guide walks through the complete deployment process for a production NextJS AI app using Vercel and Supabase. We'll cover the seven most common mistakes that waste time and money, and show you how to avoid them.
Pre-Deployment Checklist
Before touching Vercel, verify your app is production-ready.
Audit your environment variables. You'll need separate keys for development and production. Never deploy with test API keys mixed with production credentials - this is the most common mistake that breaks authentication or payment processing.
Check your Node.js version. If you're using TemplateAI or similar templates, verify the required version in package.json. The template requires Node.js 22 or higher. Set this in your Vercel project settings to avoid cryptic build failures.
Verify database migrations. You'll need to run all migrations on your production database. Missing or out-of-order migrations cause subtle bugs that are painful to debug.
Test locally one more time. Click through every feature: authentication, AI completions, image generation, payments. Anything that works locally but fails in production will cost you hours of debugging.
Supabase Production Setup
If you've been developing locally with Supabase CLI, you'll need to create a hosted Supabase project for production.
Create your production project at supabase.com/dashboard. Choose a region close to your users. Once created, you'll get new credentials - these replace your local development URLs and keys.
Run your migrations on the production database. Your template includes several critical migrations:
20231212154829_add_pgvector_search.sql- Enables pgvector extension for embeddings and vector search20231212222101_user_management_starter.sql- Creates user profile tables20231213031844_create_stripe_tables.sql- Sets up customer and subscription tracking20231212155109_add_sdxl_images.sql- Stores generated images20231219200400_add_images_storage_bucket.sql- Creates storage bucket for image uploads20231227001653_create_chats_table.sql- Stores conversation history
Run these in order using supabase db push or apply them manually through the Supabase dashboard SQL editor.
Configure authentication settings. This is critical and easy to miss. In your Supabase dashboard, go to Authentication → URL Configuration and set the Site URL to your production domain (e.g., https://yourdomain.com). If you leave this as http://localhost:3000, magic link authentication will break completely. Users will click the email link and land on localhost instead of your production site.
If you're using Google OAuth, add your production domain to the authorized JavaScript origins and redirect URIs in your Google Cloud Console OAuth settings. The callback URL should be https://your-project-ref.supabase.co/auth/v1/callback.
Common Mistake #1: Forgetting to update Supabase Site URL from localhost. This breaks magic link authentication entirely, and the error messages are vague enough that developers waste hours debugging.
Common Mistake #2: Running migrations out of order or skipping migrations. The pgvector extension must be enabled before creating tables that use vector columns. Missing this causes vector search to fail silently.
Environment Variables for Production
Your production environment needs all the keys that worked locally, but with production credentials. Here's the complete list:
NextJS configuration:
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
NODE_ENV=productionThe NEXT_PUBLIC_SITE_URL is used throughout the app for redirects, SEO metadata, and OAuth callbacks. It's defined in src/config/site.ts and referenced by authentication flows and social sharing.
Supabase credentials:
NEXT_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key_hereGet these from your Supabase project settings. The anon key is safe to expose in the browser. The service role key must be server-side only - never expose it in client code.
AI provider keys (you need at least one):
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GROQ_API_KEY=gsk_...
REPLICATE_API_TOKEN=r8_...The template supports multiple AI providers. OpenAI is required if you're using embeddings for vector search. Replicate is needed for image generation. Claude and Groq are optional alternatives for text generation.
Stripe credentials (for payments):
STRIPE_SECRET_KEY=sk_live_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...These must be live keys, not test mode keys. We'll configure the webhook in the next section.
Setting these in Vercel: Go to your project settings → Environment Variables. You can paste your entire .env file content, and Vercel will parse it automatically. Double-check that every variable is set correctly - typos here cause obscure runtime errors.
Vercel Deployment
Connect your GitHub repository to Vercel. The platform will auto-detect Next.js and use the correct build settings.
Build configuration is handled by next.config.js:
- Contentlayer processes your MDX documentation and blog posts
- Plausible analytics proxy (privacy-friendly analytics with no cookie consent needed)
- Remote image patterns for Replicate-generated images
- App Router with React strict mode
Set Node.js version to 22.x in Project Settings → General → Node.js Version. The template requires Node 22 or higher (specified in package.json engines field).
Deploy to a preview environment first. Test everything on a preview URL before promoting to production. This lets you catch issues with environment variables or API integrations without affecting live users.
Add your custom domain in Project Settings → Domains. Vercel handles SSL certificates automatically. After adding the domain, update NEXT_PUBLIC_SITE_URL to match.
Stripe Production Configuration and Webhooks
Stripe requires three steps to move from test mode to production.
Step 1: Turn off TEST mode in the Stripe dashboard (top right toggle).
Step 2: Update your API keys in Vercel environment variables. Get your live keys from stripe.com/dashboard/apikeys. Replace both the secret key and publishable key.
Step 3: Update your pricing configuration. In src/config/content.ts, the pricingPlans array includes a priceId field. This needs to be your live product's price ID (starts with price_), not the test mode price ID. Find this in your Stripe dashboard under Products → [Your Product] → Pricing.
Configure the production webhook. This is the step most developers forget, and it causes payments to succeed while users don't get access.
In your Stripe dashboard:
- Go to Developers → Webhooks
- Click "Add endpoint"
- Set endpoint URL:
https://yourdomain.com/api/webhooks/stripe - Click "Select events" and choose:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed
- Add the endpoint and copy the webhook signing secret
- Add it to Vercel as
STRIPE_WEBHOOK_SECRET
The webhook handler at src/app/api/webhooks/stripe/route.ts listens for these events and updates user access in your Supabase database. Without the webhook configured, users can complete checkout but won't get access to paid features.
To test it: Make a real purchase (use a small amount), then check your Vercel function logs and your Supabase customers and subscriptions tables. You should see new records created.
Common Mistake #3: Using Stripe test mode keys in production. Webhooks fail silently, payments appear to work, but the webhook handler rejects them because the signing secret doesn't match.
Common Mistake #4: Forgetting to configure the production webhook entirely. Payments succeed but the webhook never fires, so users don't get access to paid features.
Vector Search in Production
If your app uses vector search (like ChatGPT-style document search), you'll need to generate embeddings for your production database.
The template includes a script at src/utils/generate-embeddings.ts that:
- Reads documents from your
/docsfolder - Chunks them into smaller pieces
- Creates embeddings using OpenAI's
text-embedding-3-smallmodel (5x cheaper than previous models) - Stores embeddings in Supabase with pgvector
Run embeddings generation pointed at your production database:
npm run embeddingsMake sure your .env contains production Supabase credentials and your OpenAI API key before running this.
Cost consideration: Embeddings are cheap. For 1,000 pages of documentation, expect to pay around $0.02. You only need to re-run this when your documentation changes.
The embeddings are stored in the documents table (created by the pgvector migration). The /api/vector-search endpoint queries this table using the match_documents function to find semantically similar content.
Rate Limiting and Cost Control
Without rate limiting, a single user could rack up hundreds of dollars in API costs overnight. AI apps need different protection than traditional web apps.
Implement per-user limits. Before processing AI requests, check the user's subscription status and request count:
// Check subscription status before allowing AI generation
const user = await getUser();
const subscription = await getSubscription(user.id);
if (!subscription.hasAccess) {
const requestCount = await getRequestCount(user.id);
if (requestCount >= FREE_TIER_LIMIT) {
return Response.json({ error: 'Upgrade to continue' }, { status: 403 });
}
}Example limits: Free users get 10 AI requests per day, paid users get unlimited. Store request counts in your database with a timestamp, and reset them daily.
Cache embeddings. Don't regenerate embeddings for the same documents on every search. The template stores embeddings in Supabase, so vector search queries the existing embeddings rather than calling OpenAI repeatedly.
Monitor your costs:
- OpenAI dashboard shows token usage and costs per day
- Replicate billing page tracks image generation costs
- Supabase dashboard shows database and storage usage
- Vercel function execution logs show which API routes are called most
Expected costs for a typical AI app:
- 1,000 monthly active users: ~$50-100 in OpenAI API costs
- Heavy image generation usage: add ~$100-200 for Replicate
- Supabase Pro tier: $25/month (needed once you exceed free tier limits)
- Vercel Pro: $20/month (needed for better performance and higher function limits)
Common Mistake #5: No rate limiting. One user running a loop or a bot scanning your site could generate thousands of API requests before you notice.
Monitoring and Post-Launch Checks
Set up monitoring before you announce your launch. You want to catch errors immediately, not when users complain.
Vercel function logs show all API route executions. Check these for:
- OpenAI API errors (rate limits, invalid keys)
- Stripe webhook delivery failures
- Supabase connection errors
- Unexpected 500 errors
Supabase logs are available in the dashboard under Logs & Reporting:
- Database logs show slow queries
- Auth logs track failed login attempts
- Storage usage shows if you're approaching limits
Plausible Analytics is already integrated in next.config.js. No cookie consent required. Track page views, conversion funnels, and which features users engage with most.
Set up alerts:
- Vercel deployment notifications (Discord/Slack/email)
- Stripe payment notifications (enable in Settings → Communication Preferences)
- Supabase project alerts for database issues
Post-launch checklist - test these in production:
- Magic link authentication flow
- Google OAuth sign-in
- Make a test Stripe purchase and verify webhook delivery
- AI chat/completion features work
- Image generation (if using Replicate)
- Vector search (try CMD+K if using the ChatModal component)
- Mobile responsiveness
- All navigation links work
- SEO metadata appears correctly (view source)
Submit your sitemap to Google Search Console and Bing Webmaster Tools. The template auto-generates a sitemap at /sitemap.xml using Contentlayer, including all your documentation pages and blog posts.
Common Mistake #6: No monitoring setup. You discover critical errors only when users report them, sometimes days after deployment.
Common Mistake #7: Skipping the post-launch checklist. Small issues like broken OAuth redirects or incorrect webhook URLs waste hours of support time.
Quick Troubleshooting
Build fails in Vercel: Check Node.js version is set to 22.x. Verify package.json dependencies can install cleanly.
Authentication doesn't work: Verify Supabase Site URL is set to your production domain (not localhost). Check OAuth redirect URLs include your production domain.
Stripe payments fail silently: Test the webhook endpoint manually. Verify the webhook signing secret matches. Confirm TEST mode is OFF in Stripe dashboard.
Vector search returns no results: Confirm embeddings were generated for the production database. Check the documents table has data.
Conclusion
The template provides production-ready infrastructure out of the box. The challenge isn't the code - it's getting all the environment variables, API keys, webhooks, and configuration right.
The seven mistakes covered in this guide represent the most common deployment issues developers face. Most stem from environment variable mismatches or forgetting to update configuration from development to production values.
With this checklist, you can deploy confidently and avoid the costly debugging sessions that derail launches. The infrastructure is built. The APIs are integrated. You just need to connect the pieces correctly.
Ready to deploy? Check the full deployment documentation for additional details, or get started with TemplateAI to access the complete template with all integrations pre-built.