Deploying Next.js + Supabase to Production
Ship Next.js + Supabase to production: Vercel setup, env hygiene, safe migrations, CI/CD, and the rollback paths the docs skip.
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#
Supabase publishes a going-into-production checklist covering the platform-side items this guide does not repeat — rate limits, backups, and the security review to run before opening a project to real traffic. Read it alongside this page rather than instead of it.
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#
Per the Next.js deployment docs, Next.js runs on any Node.js host, a Docker container, or a static export, plus first-class managed platforms. There is no single "correct" platform — pick based on where the rest of your infra already lives.
Vercel#
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
Cloudflare Pages#
Pros:
- Runs on the Workers edge network (often cheaper at scale for high request volume)
- Zero-config Git integration, automatic HTTPS, preview deployments
- Good fit if the rest of your stack (KV, R2, D1, Turnstile) is already on Cloudflare
Cons:
- Requires the
@cloudflare/next-on-pagesadapter (or OpenNext) — not every Next.js feature maps 1:1 to the Workers runtime; check current compatibility before migrating - Edge runtime constraints apply: no synchronous file reads at request time, no raw TCP (
pg/postgresneed an HTTP-based driver like@supabase/supabase-js)
Best For: Teams already on Cloudflare, or optimizing for edge-network cost/latency
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. Deploying to Vercel or Cloudflare Pages#
Vercel Initial Setup#
## Install Vercel CLI
npm i -g vercel
## Login
vercel login
## Deploy
vercelProject 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.
Cloudflare Pages Alternative#
Cloudflare Pages builds Next.js through the @cloudflare/next-on-pages adapter (or OpenNext's Cloudflare target):
## Add the adapter
npm install -D @cloudflare/next-on-pages
## Build for the Workers runtime, then deploy
npx @cloudflare/next-on-pages
npx wrangler pages deploy .vercel/output/staticConnect the same GitHub repository in the Cloudflare dashboard for automatic deployments on push, with preview deployments per pull request — the workflow is equivalent to Vercel's, just running on the Workers edge runtime instead of Vercel's.
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-keyEnvironment-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',
},
}Setting Environment Variables Per Platform#
Set variables in your platform's dashboard — the concept is the same everywhere, only the UI path differs:
- Vercel: Project Settings → Environment Variables → add per environment (Production, Preview, Development) → redeploy.
- Cloudflare Pages: Workers & Pages → your project → Settings → Environment variables → add per environment (Production, Preview) → redeploy.
Redeploy after adding or changing variables — most platforms don't hot-reload env vars into an already-running deployment.
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-refCreate 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: Zero-Downtime Supabase Migrations: Expand/Contract (2026), Supabase Database Schema Design for SaaS Applications
5. CI/CD Pipelines#
See the Next.js deployment docs for the current supported deployment targets (managed platform, Node.js server, Docker, static export) — the CI/CD shape below (test → build → deploy) applies to any of them, only the deploy step changes.
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'On Cloudflare Pages, swap the deploy step for cloudflare/pages-action (or run npx wrangler pages deploy directly) — the test job above is unchanged, only the deploy action differs per platform.
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 AND sentry.server.config.ts — same init on both
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 a Domain#
The exact DNS records differ per platform (Vercel gives you an A record + CNAME, Cloudflare Pages gives you a CNAME to <project>.pages.dev and can proxy DNS itself if your domain already lives on Cloudflare), but the flow is the same:
- Go to your project's domain settings (Vercel: Project Settings → Domains; Cloudflare Pages: your project → Custom domains)
- Add your domain (e.g.,
example.com) - Add the DNS record the dashboard gives you — copy it exactly, it's specific to your project
SSL Certificate#
Both Vercel and Cloudflare Pages automatically provision and renew SSL certificates for custom domains — no manual certificate management needed on either platform.
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#
Both Vercel and Cloudflare Pages scale automatically based on traffic — you don't provision servers:
- Serverless/Workers functions scale independently per request
- Edge functions/routes deployed globally
- Static assets served from a 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 PoolingCaching 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.sqlRollback Strategy#
## Rollback to previous deployment (Vercel)
vercel rollback
## Cloudflare Pages: roll back by redeploying a previous build from the
## dashboard's Deployments tab, or check `npx wrangler pages deployment --help`
## for the current CLI rollback command (wrangler's CLI surface changes
## between versions — verify against your installed version).
## Rollback database migration
npx supabase migration downHealth 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#
Supabase's own going-into-production checklist and the Next.js headers() config reference cover the platform-level and framework-level pieces this section only summarizes — read both before shipping.
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_KEYAPI 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://www.iloveblogs.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 buildEnvironment 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 (built by the Next.js team) and Cloudflare Pages are both solid choices — both offer zero-config or near-zero-config deployment, automatic HTTPS, preview deployments for pull requests, and a global edge network. Vercel has the tightest first-party Next.js integration; Cloudflare Pages is a good fit if the rest of your stack is already on Cloudflare. Check the Next.js deployment docs for current platform support.
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 roll back via the dashboard. On Cloudflare Pages, redeploy a previous build from the dashboard's deployment history. 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.
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.
Further reading: Production RAG with Supabase pgvector and Next.js.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Related Guides
Next.js + Supabase Production Launch Checklist (47 Items)
47-check pre-launch audit for Next.js 15 + Supabase: security, RLS, performance, observability, auth — every item caused a real incident.
Complete Guide to Building SaaS with Next.js and Supabase
Build a production SaaS with Next.js 15 and Supabase: auth, multi-tenant data, billing, and the deploy checklist that actually ships.
Error Handling and Observability for Next.js + Supabase
Comprehensive guide to error handling, logging, monitoring, and observability for production Next.js and Supabase applications.

