Deploying Next.js + Supabase to Production
Complete guide to deploying Next.js and Supabase applications to production. Learn Vercel deployment, environment configuration, database migrations, CI/CD...
Deploying Next.js + Supabase to Production#
Deploying to production is more than pushing code—it's about reliability, performance, and maintainability. This comprehensive guide covers everything you need to deploy Next.js + Supabase applications with confidence.
Why Proper Deployment Matters#
Reliability:
- Zero-downtime deployments
- Automatic rollbacks on failure
- Health checks and monitoring
Performance:
- Edge deployment for low latency
- Optimized builds
- CDN for static assets
Maintainability:
- Automated CI/CD pipelines
- Environment management
- Database migrations
1. Deployment Platforms Overview#
Vercel (Recommended)#
Pros:
- Built by Next.js creators
- Zero-config deployment
- Automatic HTTPS
- Edge network
- Preview deployments
- Free tier available
Best For: Next.js applications, indie developers, startups
Netlify#
Pros:
- Simple deployment
- Good free tier
- Edge functions
- Form handling
Best For: Static sites, JAMstack applications
AWS (Advanced)#
Pros:
- Full control
- Scalable
- Many services
Cons:
- Complex setup
- Higher cost
- Steeper learning curve
Best For: Enterprise applications, specific requirements
Self-Hosted#
Pros:
- Complete control
- Cost-effective at scale
Cons:
- Requires DevOps expertise
- Maintenance overhead
Best For: Large applications, specific compliance needs
2. Vercel Deployment#
Initial Setup#
## Install Vercel CLI
npm i -g vercel
## Login
vercel login
## Deploy
vercel
Project Configuration#
// vercel.json
{
"buildCommand": "npm run build",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "nextjs",
"regions": ["iad1"],
"env": {
"NEXT_PUBLIC_SUPABASE_URL": "@supabase-url",
"NEXT_PUBLIC_SUPABASE_ANON_KEY": "@supabase-anon-key"
}
}
Automatic Deployments#
Connect GitHub repository:
- Go to Vercel dashboard
- Click "New Project"
- Import Git repository
- Configure build settings
- Deploy
Every push to main branch triggers deployment.
Related: Deploy Next.js Supabase App to Vercel Production, Configure Custom Domain for Next.js on Vercel
3. Environment Configuration#
Environment Variables#
## .env.local (development)
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-local-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-local-service-key
## Production (set in Vercel dashboard)
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-prod-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-prod-service-key
Environment-Specific Configuration#
// lib/config.ts
export const config = {
supabase: {
url: process.env.NEXT_PUBLIC_SUPABASE_URL!,
anonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
},
app: {
url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
env: process.env.NODE_ENV,
},
features: {
analytics: process.env.NODE_ENV === 'production',
debug: process.env.NODE_ENV === 'development',
},
}
Vercel Environment Variables#
Set in Vercel dashboard:
- Go to Project Settings
- Click "Environment Variables"
- Add variables for Production, Preview, Development
- Redeploy to apply changes
Related: Implement Environment Variables Best Practices Next.js, Deploy Next.js 15 to Vercel Without Environment Variable Errors
4. Database Migrations#
Supabase CLI Setup#
## Install Supabase CLI
npm install supabase --save-dev
## Initialize Supabase
npx supabase init
## Link to remote project
npx supabase link --project-ref your-project-ref
Create Migrations#
## Create new migration
npx supabase migration new add_posts_table
-- supabase/migrations/20260216000000_add_posts_table.sql
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title TEXT NOT NULL,
content TEXT,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- RLS policies
CREATE POLICY "Users can view own posts"
ON posts FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can create posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
Apply Migrations#
## Apply to local database
npx supabase db push
## Apply to production
npx supabase db push --db-url "postgresql://..."
Migration Best Practices#
-- ✅ Good: Reversible migrations
CREATE TABLE posts (...);
-- Add rollback in separate file
-- supabase/migrations/20260216000001_rollback_posts.sql
DROP TABLE IF EXISTS posts;
-- ✅ Good: Add columns with defaults
ALTER TABLE posts ADD COLUMN status TEXT DEFAULT 'draft';
-- ❌ Bad: Breaking changes without migration path
ALTER TABLE posts DROP COLUMN important_field;
Related: Handle Database Migrations in Production Supabase, Supabase Database Schema Design for SaaS Applications
5. CI/CD Pipelines#
GitHub Actions#
## .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run linter
run: npm run lint
- name: Type check
run: npm run type-check
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to Vercel
uses: amondnet/vercel-action@v20
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
Database Migration in CI/CD#
## .github/workflows/migrate.yml
name: Database Migration
on:
push:
branches: [main]
paths:
- 'supabase/migrations/**'
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
- name: Run migrations
run: npx supabase db push --db-url ${{ secrets.DATABASE_URL }}
Related: Set Up CI/CD Pipeline for Next.js with GitHub Actions, Deploy Next.js Supabase App to Vercel Production
6. Monitoring and Logging#
Error Tracking with Sentry#
npm install @sentry/nextjs
// sentry.client.config.ts
import * as Sentry from "@sentry/nextjs"
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
environment: process.env.NODE_ENV,
})
// sentry.server.config.ts
import * as Sentry from "@sentry/nextjs"
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
environment: process.env.NODE_ENV,
})
Application Logging#
// lib/logger.ts
type LogLevel = 'info' | 'warn' | 'error'
export function log(level: LogLevel, message: string, meta?: any) {
const timestamp = new Date().toISOString()
const logEntry = {
timestamp,
level,
message,
...meta,
}
if (process.env.NODE_ENV === 'production') {
// Send to logging service
fetch('/api/logs', {
method: 'POST',
body: JSON.stringify(logEntry),
})
} else {
console[level](logEntry)
}
}
Performance Monitoring#
// lib/monitoring.ts
export function trackPerformance(metric: string, value: number) {
if (process.env.NODE_ENV === 'production') {
// Send to analytics
fetch('/api/metrics', {
method: 'POST',
body: JSON.stringify({ metric, value, timestamp: Date.now() }),
})
}
}
// Usage
const start = Date.now()
await fetchData()
trackPerformance('data_fetch_time', Date.now() - start)
Related: Monitor Next.js Application Performance in Production, Set Up Error Tracking with Sentry in Next.js
7. Custom Domain Configuration#
Add Domain to Vercel#
- Go to Project Settings → Domains
- Add your domain (e.g.,
example.com) - Configure DNS records:
Type: A
Name: @
Value: 76.76.21.21
Type: CNAME
Name: www
Value: cname.vercel-dns.com
SSL Certificate#
Vercel automatically provisions SSL certificates via Let's Encrypt.
Redirect Configuration#
// next.config.mjs
export default {
async redirects() {
return [
{
source: '/:path*',
has: [
{
type: 'host',
value: 'www.example.com',
},
],
destination: 'https://example.com/:path*',
permanent: true,
},
]
},
}
Related: Configure Custom Domain for Next.js on Vercel, Deploy Next.js Supabase App to Vercel Production
8. Scaling Strategies#
Horizontal Scaling#
Vercel automatically scales based on traffic:
- Serverless functions scale independently
- Edge functions deployed globally
- Static assets served from CDN
Database Scaling#
-- Add indexes for frequently queried columns
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at DESC);
-- Use connection pooling
-- Configure in Supabase dashboard: Database → Connection Pooling
Caching Strategy#
// app/posts/page.tsx
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 }, // Revalidate every 60 seconds
})
return res.json()
}
Edge Functions#
// app/api/edge/route.ts
export const runtime = 'edge'
export async function GET(request: Request) {
// Runs on edge, close to users
return new Response('Hello from the edge!')
}
Related: Next.js Edge Runtime vs Node Runtime When to Use, Supabase Database Query Optimization
9. Disaster Recovery#
Database Backups#
Supabase automatically backs up your database:
- Point-in-time recovery (PITR) available on Pro plan
- Daily backups retained for 7 days
- Manual backups via CLI
## Create manual backup
npx supabase db dump -f backup.sql
## Restore from backup
psql -h db.your-project.supabase.co -U postgres -d postgres -f backup.sql
Rollback Strategy#
## Rollback to previous deployment (Vercel)
vercel rollback
## Rollback database migration
npx supabase migration down
Health Checks#
// app/api/health/route.ts
import { createClient } from '@/lib/supabase/server'
export async function GET() {
try {
const supabase = createClient()
// Check database connection
const { error } = await supabase.from('posts').select('count').limit(1)
if (error) throw error
return Response.json({ status: 'healthy' })
} catch (error) {
return Response.json({ status: 'unhealthy', error: error.message }, { status: 500 })
}
}
10. Security Best Practices#
Environment Variables#
// ✅ Good: Server-side only
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY
// ❌ Bad: Exposed to client
const serviceKey = process.env.NEXT_PUBLIC_SERVICE_KEY
API Rate Limiting#
// lib/rate-limit.ts
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'),
})
export async function checkRateLimit(identifier: string) {
const { success } = await ratelimit.limit(identifier)
return success
}
CORS Configuration#
// middleware.ts
export function middleware(request: Request) {
const response = NextResponse.next()
response.headers.set('Access-Control-Allow-Origin', 'https://iloveblog.blog')
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
return response
}
Security Headers#
// next.config.mjs
export default {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin',
},
],
},
]
},
}
Related: Implement API Rate Limiting in Next.js Applications, Implement Environment Variables Best Practices Next.js
11. Common Deployment Issues#
Build Errors#
Problem: Module not found after deploy
Solution:
## Clear cache and rebuild
rm -rf .next node_modules
npm install
npm run build
Environment Variable Issues#
Problem: Environment variables not available
Solution:
- Ensure variables are set in Vercel dashboard
- Prefix client-side variables with
NEXT_PUBLIC_ - Redeploy after adding variables
Database Connection Issues#
Problem: Can't connect to Supabase
Solution:
// Check connection pooling settings
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
db: {
schema: 'public',
},
auth: {
persistSession: true,
},
}
)
Related: Fix Next.js Build Error Module Not Found After Deploy, Deploy Next.js 15 to Vercel Without Environment Variable Errors
12. Deployment Checklist#
Pre-Deployment#
- [ ] All tests passing
- [ ] Linter passing
- [ ] Type checking passing
- [ ] Environment variables configured
- [ ] Database migrations ready
- [ ] Security headers configured
- [ ] Error tracking setup
- [ ] Performance monitoring setup
Deployment#
- [ ] Deploy to staging first
- [ ] Run smoke tests
- [ ] Check error logs
- [ ] Verify database migrations
- [ ] Test critical user flows
- [ ] Monitor performance metrics
Post-Deployment#
- [ ] Verify production deployment
- [ ] Check error rates
- [ ] Monitor performance
- [ ] Test from different locations
- [ ] Verify SSL certificate
- [ ] Check custom domain
- [ ] Monitor database performance
Frequently Asked Questions (FAQ)#
What is the best platform to deploy Next.js with Supabase?#
Vercel is the recommended platform for Next.js applications. It's built by the Next.js team, offers zero-config deployment, automatic HTTPS, preview deployments for pull requests, and global edge network distribution. The free tier is generous for indie developers.
How do I handle environment variables in production?#
Set environment variables in your deployment platform's dashboard (Vercel, Netlify, etc.). Use NEXT_PUBLIC_ prefix for client-side variables. Never commit secrets to Git. Use different values for development, staging, and production environments.
Should I run database migrations before or after deployment?#
Run migrations before deployment in a separate step. Use Supabase CLI with npx supabase db push in your CI/CD pipeline. Test migrations in staging first, and always create rollback migrations for safety.
How do I implement zero-downtime deployments?#
Use platforms like Vercel that support atomic deployments. They deploy to new infrastructure, run health checks, then switch traffic. If deployment fails, traffic stays on the old version. Always test in staging first.
What monitoring should I set up for production?#
Implement error tracking (Sentry), performance monitoring (Vercel Analytics), uptime monitoring (UptimeRobot), and database monitoring (Supabase Dashboard). Set up alerts for critical errors, high response times, and downtime.
How do I handle database backups?#
Supabase automatically backs up your database daily (retained for 7 days on free tier, longer on paid plans). Pro plan includes Point-in-Time Recovery (PITR). Create manual backups before major migrations using npx supabase db dump.
Should I use connection pooling in production?#
Yes, always enable connection pooling for production. It prevents connection limit errors under load and improves performance. Enable it in Supabase Dashboard → Database → Connection Pooling and use the pooled connection string.
How do I rollback a failed deployment?#
On Vercel, use vercel rollback or rollback via dashboard. For database migrations, run your rollback migration with npx supabase migration down. Always test rollback procedures in staging.
What security headers should I configure?#
Configure X-Frame-Options (DENY), X-Content-Type-Options (nosniff), Referrer-Policy (origin-when-cross-origin), and Content-Security-Policy. Use next.config.js headers() function to set these for all routes.
How do I optimize build times in CI/CD?#
Cache node_modules and .next directories, use npm ci instead of npm install, enable Turbopack for faster builds, and run tests in parallel. Consider using GitHub Actions cache or Vercel's built-in caching.
Should I use preview deployments?#
Yes, preview deployments are essential for testing changes before production. They create a unique URL for each pull request, allowing you to test features, run E2E tests, and get stakeholder approval before merging.
How do I handle secrets in CI/CD?#
Use your CI/CD platform's secrets management (GitHub Secrets, GitLab CI/CD variables). Never commit secrets to Git. Use different secrets for staging and production. Rotate secrets regularly.
What's the best way to monitor database performance?#
Use Supabase Dashboard's Query Performance tab to identify slow queries. Monitor connection count, query execution time, and database size. Set up alerts for high CPU usage or connection limits.
How do I implement health checks?#
Create a /api/health endpoint that checks database connectivity, external service availability, and critical functionality. Use this endpoint for uptime monitoring and deployment verification.
Related Articles#
- Complete Guide to Building SaaS with Next.js and Supabase
- Next.js Performance Optimization for Indie Developers
- Supabase Authentication & Authorization Patterns
- Set Up CI/CD Pipeline for Next.js with GitHub Actions
Conclusion#
Deploying to production is a critical step in your application's lifecycle. With Vercel and Supabase, you get a powerful, scalable infrastructure without managing servers.
Focus on these key areas: automated deployments, proper environment configuration, database migrations, monitoring, and security. Start simple, then add complexity as needed.
Ship with confidence. Deploy today.