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 buildThis 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
- Go to vercel.com and sign in.
- Click Add New Project.
- Import your Git repository (GitHub, GitLab, or Bitbucket).
- 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:
| Variable | Environment |
|---|---|
DATABASE_URL | Production, Preview |
AUTH_SECRET | Production, Preview |
AUTH_GITHUB_ID | Production, Preview |
AUTH_GITHUB_SECRET | Production, Preview |
NEXT_PUBLIC_APP_URL | Production |
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 mainVercel 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
- You push a branch or open a PR.
- Vercel builds that branch and gives it a URL like
your-project-git-feature-branch.vercel.app. - Teammates can review the live preview before merging.
- 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:
- Add your domain (e.g.,
myapp.com). - Configure DNS: add the CNAME or A record Vercel provides.
- Vercel automatically provisions an SSL certificate.
# DNS Configuration
Type: CNAME
Name: @
Value: cname.vercel-dns.comBuild 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.logoutput 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 mainRollbacks
If a deployment has issues, Vercel makes rollbacks simple:
- Go to Deployments in the Vercel dashboard.
- Find the last working deployment.
- Click the three-dot menu and select Promote to Production.
This instantly serves the previous deployment without rebuilding.
Summary
- Run
pnpm buildlocally 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 deployas 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.