Building a Next.js app is one thing; deploying it reliably to production is another. This guide covers the main deployment paths — Vercel (easiest) and self-hosting with Docker (most control) — plus the configuration and optimization that matter for a production Next.js app in 2026.
📋 Table of Contents
Deployment Options Overview
| Method | Best For | Effort |
|---|---|---|
| Vercel | Fastest deploy, zero config, edge | Minimal |
| Docker self-host | Full control, cost at scale | Moderate |
| Node server | Simple VPS hosting | Low-Moderate |
| Static export | Fully static sites | Low |
Method 1: Vercel (Easiest)
# Install Vercel CLI
npm install -g vercel
# Deploy from your project directory
vercel # preview deployment
vercel --prod # production deployment
# Or connect your Git repo in the Vercel dashboard for
# automatic deployments on every push
Vercel (made by Next.js’s creators) handles builds, edge caching, serverless functions, and image optimization automatically. Set environment variables in the dashboard. It’s the fastest path to a production Next.js app with zero infrastructure management.
Method 2: Docker Self-Hosting
For full control and predictable costs at scale, self-host with Docker. Next.js’s standalone output makes this efficient:
// next.config.js — enable standalone output
module.exports = {
output: 'standalone', // bundles only needed files for a small image
};
# Dockerfile — multi-stage for a small production image
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# Copy standalone output (much smaller than full node_modules)
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
# Build and run
docker build -t my-nextjs-app .
docker run -p 3000:3000 --env-file .env.production my-nextjs-app
Nginx Reverse Proxy for Self-Hosting
# /etc/nginx/sites-enabled/myapp
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Cache Next.js static assets aggressively
location /_next/static {
proxy_pass http://localhost:3000;
add_header Cache-Control "public, max-age=31536000, immutable";
}
}
Environment Variables in Production
# Server-only secrets (never exposed to browser)
DATABASE_URL=postgresql://...
API_SECRET=...
# Client-exposed vars need NEXT_PUBLIC_ prefix (these are PUBLIC)
NEXT_PUBLIC_API_URL=https://api.example.com
# Set these in Vercel dashboard, or pass via --env-file to Docker
# NEVER commit production secrets
Caching and Revalidation
// Incremental Static Regeneration — cache pages, revalidate periodically
export const revalidate = 3600; // regenerate at most once per hour
// Or on-demand revalidation via API
import { revalidatePath } from 'next/cache';
export async function POST(request) {
// After content changes, revalidate the affected page
revalidatePath('/blog');
return Response.json({ revalidated: true });
}
// Fetch caching
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 }, // cache this fetch for 60 seconds
});
Production Checklist
- Enable standalone output for smaller Docker images
- Set all environment variables in your platform (not committed)
- Configure caching — ISR for pages, cache headers for static assets
- Optimize images — use next/image (automatic on Vercel; configure a loader when self-hosting)
- Add error monitoring — Sentry or similar for production errors
- Set up HTTPS — automatic on Vercel; Let’s Encrypt for self-hosting
- Enable compression — gzip/brotli at the proxy level
Frequently Asked Questions
Q: Vercel or self-hosting?
A: Vercel for the fastest deploy and zero infrastructure management (great for most apps). Self-hosting with Docker for full control, predictable costs at scale, or specific infrastructure requirements. Start with Vercel; move to self-hosting if costs or control demand it.
Q: Does self-hosted Next.js support all features?
A: Most, yes — SSR, ISR, API routes, and image optimization all work self-hosted (image optimization needs a configured loader). A few Vercel-specific edge features differ, but core Next.js works fully when self-hosted.
Q: Why is my self-hosted image optimization not working?
A: next/image optimization is automatic on Vercel but needs configuration when self-hosted — either use the built-in optimizer (requires sharp) or configure an external image loader/CDN. Check the next.config.js images settings.
Q: How do I handle environment variables that differ per environment?
A: Set them in your hosting platform (Vercel dashboard, Docker –env-file, or your orchestrator). Use NEXT_PUBLIC_ prefix only for values safe to expose to the browser. Keep secrets server-side.
Q: How do I reduce cold starts?
A: On Vercel, serverless functions have some cold start; keep them lean. Self-hosted with a long-running Node server has no cold starts. For ISR/static pages, most requests are served from cache instantly regardless.
Conclusion
Deploying Next.js to production in 2026 has two main paths: Vercel for the fastest, zero-config deploy with automatic optimization and edge caching, or Docker self-hosting for full control and predictable costs using standalone output for small images behind an nginx proxy. Whichever you choose, set environment variables in the platform (never committed), configure caching with ISR and cache headers, optimize images, and add error monitoring. Start with Vercel for simplicity, and move to self-hosting when cost or control requirements justify the added operational work.
📚 You might also like
🔗 Share this article




✍️ Leave a Comment