Skip to main content

Deployment

Once your full-stack Next.js app is ready, you need to deploy it. Vercel is the platform built by the creators of Next.js and offers the smoothest deployment experience, but the concepts here apply to other platforms too.

Preparing for Deployment

Before deploying, make sure your project is production-ready.

Run a Production Build Locally

pnpm build

This catches errors that only appear during the build: missing environment variables, type errors in Server Components, invalid dynamic routes, and more. Fix anything that fails before pushing.

Check Your Environment Variables

List every variable your app needs. Create an .env.example file as documentation:

# Database
DATABASE_URL=

# Auth.js
AUTH_SECRET=
AUTH_GITHUB_ID=
AUTH_GITHUB_SECRET=

# Public variables (exposed to the browser)
NEXT_PUBLIC_APP_URL=

Remember: variables prefixed with NEXT_PUBLIC_ are bundled into the client-side JavaScript and visible to anyone. Never put secrets in NEXT_PUBLIC_ variables.

Deploying to Vercel

Connect Your Repository

  1. Go to vercel.com and sign in.
  2. Click Add New Project.
  3. Import your Git repository (GitHub, GitLab, or Bitbucket).
  4. Vercel auto-detects it as a Next.js project and configures build settings.

Configure Environment Variables

In your Vercel project settings, go to Settings > Environment Variables and add each variable:

VariableEnvironment
DATABASE_URLProduction, Preview
AUTH_SECRETProduction, Preview
AUTH_GITHUB_IDProduction, Preview
AUTH_GITHUB_SECRETProduction, Preview
NEXT_PUBLIC_APP_URLProduction

You can set different values per environment. For example, use a staging database URL for Preview deployments.

Deploy

Push to your main branch:

git push origin main

Vercel automatically builds and deploys. Your app is live at your-project.vercel.app.

Preview Deployments

Every pull request and branch push gets its own preview deployment with a unique URL. This is one of the most powerful features for teams.

How It Works

  1. You push a branch or open a PR.
  2. Vercel builds that branch and gives it a URL like your-project-git-feature-branch.vercel.app.
  3. Teammates can review the live preview before merging.
  4. When the PR is merged, the main branch deploys to production automatically.

Preview-Specific Environment Variables

Set environment variables that only apply to Preview deployments. This is useful for pointing previews at a staging database:

# Production
DATABASE_URL=postgresql://user:pass@prod-host:5432/prod_db

# Preview
DATABASE_URL=postgresql://user:pass@staging-host:5432/staging_db

Custom Domains

Add a custom domain in Settings > Domains:

  1. Add your domain (e.g., myapp.com).
  2. Configure DNS: add the CNAME or A record Vercel provides.
  3. Vercel automatically provisions an SSL certificate.
# DNS Configuration
Type: CNAME
Name: @
Value: cname.vercel-dns.com

Build Configuration

Customizing the Build

In vercel.json, you can customize build behavior:

{
  "buildCommand": "prisma generate && next build",
  "installCommand": "pnpm install",
  "framework": "nextjs"
}

Ignoring Builds

Skip builds for certain changes (e.g., docs-only updates):

{
  "ignoreCommand": "git diff --quiet HEAD^ HEAD -- app/ lib/ prisma/"
}

This only triggers a build when files in app/, lib/, or prisma/ change.

Database Migrations in Production

Run migrations as part of your build process:

{
  "buildCommand": "prisma migrate deploy && next build"
}

prisma migrate deploy applies pending migrations without creating new ones. It is safe for production.

Monitoring and Logs

Vercel Logs

View real-time and historical logs in the Vercel dashboard under Deployments > Logs. You can filter by:

  • Build logs - Output from the build process.
  • Runtime logs - console.log output from Server Components, Route Handlers, and Server Actions.
  • Edge logs - Output from middleware.

Adding Structured Logging

Use structured logging to make it easier to search and filter logs:

function log(level: "info" | "warn" | "error", message: string, data?: Record<string, unknown>) {
  const entry = {
    level,
    message,
    timestamp: new Date().toISOString(),
    ...data,
  };
  console.log(JSON.stringify(entry));
}

// Usage
log("info", "Post created", { postId: "abc123", userId: "user456" });
log("error", "Failed to fetch data", { url: "/api/posts", status: 500 });

Health Check Endpoint

Create a simple health check route at app/api/health/route.ts:

import { prisma } from "@/app/lib/db";
import { NextResponse } from "next/server";

export async function GET() {
  try {
    await prisma.$queryRaw`SELECT 1`;
    return NextResponse.json({
      status: "healthy",
      timestamp: new Date().toISOString(),
    });
  } catch {
    return NextResponse.json(
      { status: "unhealthy", timestamp: new Date().toISOString() },
      { status: 503 }
    );
  }
}

Performance Checklist

Before going live, review this checklist:

# 1. Run a production build to check for errors
pnpm build

# 2. Analyze the bundle
ANALYZE=true pnpm build  # with @next/bundle-analyzer

# 3. Run Lighthouse
# Open Chrome DevTools > Lighthouse tab

# 4. Check that all env vars are set in Vercel

# 5. Verify database migrations are applied
npx prisma migrate status

# 6. Test the preview deployment before merging to main

Rollbacks

If a deployment has issues, Vercel makes rollbacks simple:

  1. Go to Deployments in the Vercel dashboard.
  2. Find the last working deployment.
  3. Click the three-dot menu and select Promote to Production.

This instantly serves the previous deployment without rebuilding.

Summary

  • Run pnpm build locally before deploying to catch build errors early.
  • Set all environment variables in Vercel, with different values for Production and Preview.
  • Every branch and PR gets an automatic preview deployment with its own URL.
  • Run prisma migrate deploy as part of your build command for database migrations.
  • Use structured logging and a health check endpoint for production monitoring.
  • Vercel supports instant rollbacks by promoting a previous deployment.