Deploying Next.js + Supabase to Production
Developer Guide

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...

2026-02-16
38 min read
Deploying Next.js + Supabase to Production

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#

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:

  1. Go to Vercel dashboard
  2. Click "New Project"
  3. Import Git repository
  4. Configure build settings
  5. 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:

  1. Go to Project Settings
  2. Click "Environment Variables"
  3. Add variables for Production, Preview, Development
  4. 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#

  1. Go to Project Settings → Domains
  2. Add your domain (e.g., example.com)
  3. 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.

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.

Frequently Asked Questions

|

Have more questions? Contact us