|$ curl https://forge-ai.dev/api/markdown?path=docs/nextjs/deployment
$cat docs/next.js-deployment.md
updated Recentlyยท35 min readยทpublished

Next.js Deployment

โ—†Next.jsโ—†Deploymentโ—†DevOpsโ—†Intermediate to Advanced๐ŸŽฏFree Tools
Deployment Options

Next.js can be deployed anywhere that supports Node.js. The most common options are Vercel (the platform built by the creators of Next.js), self-hosted Node.js servers, Docker containers, and serverless platforms. Each approach has different tradeoffs for complexity, cost, and control.

This guide covers all major deployment strategies, from zero-config Vercel deployments to fully self-managed Docker setups.

โ„น

info

Vercel provides the most complete Next.js deployment experience with automatic preview deployments, analytics, and edge functions. For teams that need full control over infrastructure, self-hosted or Docker deployments are the way to go.
Deploying to Vercel

Vercel is the recommended deployment platform for Next.js. It provides zero-config deployment, automatic preview URLs for PRs, edge functions, and deep integration with Next.js features.

terminal
Bash
1# Install Vercel CLI
2npm i -g vercel
3
4# Deploy (interactive setup)
5vercel
6
7# Deploy to production
8vercel --prod
9
10# Deploy with environment variables
11vercel --prod -e DATABASE_URL=postgresql://... -e API_KEY=sk-...
12
13# Link to an existing project
14vercel link
15vercel deploy --prod

Or deploy via the Vercel dashboard:

1.Connect your Git repository (GitHub, GitLab, Bitbucket)
2.Vercel auto-detects Next.js and configures the build
3.Every push triggers a deployment
4.PRs get automatic preview deployments
5.Production deployments happen on merge to main
Production Configuration

Configure Next.js for production in next.config.ts. Key options include output mode, image optimization, headers, and redirects.

next.config.ts
TypeScript
1// next.config.ts
2import type { NextConfig } from "next";
3
4const nextConfig: NextConfig = {
5 // Output mode for self-hosted deployments
6 output: "standalone",
7
8 // Image optimization
9 images: {
10 remotePatterns: [
11 {
12 protocol: "https",
13 hostname: "**.example.com",
14 },
15 {
16 protocol: "https",
17 hostname: "images.unsplash.com",
18 },
19 ],
20 formats: ["image/avif", "image/webp"],
21 minimumCacheTTL: 60 * 60 * 24 * 30, // 30 days
22 },
23
24 // Security headers
25 async headers() {
26 return [
27 {
28 source: "/(.*)",
29 headers: [
30 { key: "X-Frame-Options", value: "DENY" },
31 { key: "X-Content-Type-Options", value: "nosniff" },
32 { key: "Referrer-Policy", value: "origin-when-cross-origin" },
33 {
34 key: "Content-Security-Policy",
35 value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline';",
36 },
37 ],
38 },
39 ];
40 },
41
42 // Redirects
43 async redirects() {
44 return [
45 { source: "/old-blog/:slug", destination: "/blog/:slug", permanent: true },
46 { source: "/home", destination: "/", permanent: true },
47 ];
48 },
49
50 // Rewrites (proxy)
51 async rewrites() {
52 return [
53 {
54 source: "/api/legacy/:path*",
55 destination: "https://legacy-api.example.com/:path*",
56 },
57 ];
58 },
59
60 // Experimental features
61 experimental: {
62 // Enable PPR (Partial Prerendering)
63 ppr: true,
64 },
65};
66
67export default nextConfig;
Standalone Output (Self-Hosted)

The standalone output mode creates a minimal deployment package that includes only the files needed to run your app. No node_modules directory is required in production โ€” all dependencies are bundled into the output.

terminal
Bash
1# Build with standalone output
2npm run build
3
4# The standalone output is in .next/standalone/
5# It includes a minimal Node.js server
6ls .next/standalone/
7
8# Copy static assets and public files
9cp -r .next/static .next/standalone/.next/static
10cp -r public .next/standalone/public
11
12# Start the server
13node .next/standalone/server.js
next.config.ts
TypeScript
1// next.config.ts โ€” Enable standalone output
2const nextConfig: NextConfig = {
3 output: "standalone",
4};
โ„น

info

Standalone output is essential for Docker deployments. It reduces the deployment size from hundreds of megabytes (with node_modules) to around 20-30MB, making container builds significantly faster.
Docker Deployment

Deploy Next.js in a Docker container for consistent, reproducible environments. Use the standalone output mode for minimal image size.

Dockerfile
Dockerfile
1# Dockerfile for Next.js standalone output
2
3# Stage 1: Dependencies
4FROM node:20-alpine AS deps
5WORKDIR /app
6COPY package.json package-lock.json ./
7RUN npm ci --only=production
8
9# Stage 2: Build
10FROM node:20-alpine AS builder
11WORKDIR /app
12COPY --from=deps /app/node_modules ./node_modules
13COPY . .
14
15# Set build-time environment variables
16ENV NEXT_TELEMETRY_DISABLED=1
17ENV NODE_ENV=production
18
19RUN npm run build
20
21# Stage 3: Production
22FROM node:20-alpine AS runner
23WORKDIR /app
24
25ENV NODE_ENV=production
26ENV NEXT_TELEMETRY_DISABLED=1
27
28RUN addgroup --system --gid 1001 nodejs
29RUN adduser --system --uid 1001 nextjs
30
31# Copy standalone output
32COPY --from=builder /app/.next/standalone ./
33COPY --from=builder /app/.next/static ./.next/static
34COPY --from=builder /app/public ./public
35
36USER nextjs
37
38EXPOSE 3000
39ENV PORT=3000
40ENV HOSTNAME="0.0.0.0"
41
42CMD ["node", "server.js"]
docker-compose.yml
YAML
1# docker-compose.yml
2services:
3 app:
4 build: .
5 ports:
6 - "3000:3000"
7 environment:
8 - DATABASE_URL=postgresql://user:pass@db:5432/mydb
9 - NEXT_PUBLIC_API_URL=https://api.example.com
10 depends_on:
11 - db
12
13 db:
14 image: postgres:16-alpine
15 environment:
16 POSTGRES_USER: user
17 POSTGRES_PASSWORD: pass
18 POSTGRES_DB: mydb
19 volumes:
20 - postgres_data:/var/lib/postgresql/data
21
22volumes:
23 postgres_data:
โœ“

best practice

Use multi-stage Docker builds to keep the production image small. The build stage includes dev dependencies and build tools, while the production stage only includes the standalone output and static assets.
Self-Hosted Node.js

Deploy Next.js on your own server (VPS, bare metal, or VM) with a process manager like PM2 for production reliability.

terminal
Bash
1# Build the application
2npm run build
3
4# Start with PM2 (process manager)
5npm i -g pm2
6
7# Start the production server
8pm2 start npm --name "nextjs-app" -- start
9
10# Or start with standalone output
11pm2 start .next/standalone/server.js --name "nextjs-app"
12
13# Save PM2 process list (survives server restarts)
14pm2 save
15
16# Set up PM2 to start on boot
17pm2 startup
18
19# Useful PM2 commands
20pm2 status # View running processes
21pm2 logs nextjs-app # View logs
22pm2 restart nextjs-app # Restart
23pm2 stop nextjs-app # Stop
24pm2 delete nextjs-app # Remove
nginx.conf
NGINX
1# /etc/nginx/conf.d/nextjs.conf
2# Nginx reverse proxy for Next.js
3
4upstream nextjs {
5 server 127.0.0.1:3000;
6}
7
8server {
9 listen 80;
10 server_name example.com;
11
12 # Redirect HTTP to HTTPS
13 return 301 https://$server_name$request_uri;
14}
15
16server {
17 listen 443 ssl http2;
18 server_name example.com;
19
20 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
21 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
22
23 # Security headers
24 add_header X-Frame-Options "SAMEORIGIN" always;
25 add_header X-Content-Type-Options "nosniff" always;
26
27 # Proxy to Next.js
28 location / {
29 proxy_pass http://nextjs;
30 proxy_http_version 1.1;
31 proxy_set_header Upgrade $http_upgrade;
32 proxy_set_header Connection "upgrade";
33 proxy_set_header Host $host;
34 proxy_set_header X-Real-IP $remote_addr;
35 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
36 proxy_set_header X-Forwarded-Proto $scheme;
37 proxy_cache_bypass $http_upgrade;
38 }
39
40 # Cache static assets
41 location /_next/static/ {
42 proxy_pass http://nextjs;
43 proxy_cache_valid 200 365d;
44 add_header Cache-Control "public, max-age=31536000, immutable";
45 }
46
47 # Cache images
48 location /images/ {
49 proxy_pass http://nextjs;
50 proxy_cache_valid 200 30d;
51 }
52}
Environment Variables in Production

Environment variables must be set at build time and runtime. Some variables are inlined into the client bundle during build, while server-only variables are read at runtime.

.env.production.local
Bash
1# .env.production.local (never committed to git)
2
3# Server-only (available at runtime)
4DATABASE_URL=postgresql://prod-user:pass@db.example.com:5432/proddb
5API_SECRET=sk-production-key
6JWT_SECRET=your-jwt-secret
7
8# Client-side (inlined at build time)
9NEXT_PUBLIC_API_URL=https://api.example.com
10NEXT_PUBLIC_SENTRY_DSN=https://xxx@sentry.io/123
โš 

warning

NEXT_PUBLIC_ variables are inlined at build time and embedded in the client bundle. They cannot be changed without rebuilding. Server-only variables (without the prefix) are read at runtime and can be changed via environment variables on your hosting platform.
CI/CD Pipeline

Set up continuous deployment with GitHub Actions or a similar CI/CD platform.

.github/workflows/deploy.yml
YAML
1# .github/workflows/deploy.yml
2name: Deploy
3
4on:
5 push:
6 branches: [main]
7 pull_request:
8 branches: [main]
9
10jobs:
11 test:
12 runs-on: ubuntu-latest
13 steps:
14 - uses: actions/checkout@v4
15 - uses: actions/setup-node@v4
16 with:
17 node-version: 20
18 cache: npm
19 - run: npm ci
20 - run: npm run lint
21 - run: npm run typecheck
22 - run: npm test
23
24 deploy:
25 needs: test
26 if: github.ref == 'refs/heads/main'
27 runs-on: ubuntu-latest
28 steps:
29 - uses: actions/checkout@v4
30 - uses: actions/setup-node@v4
31 with:
32 node-version: 20
33 cache: npm
34 - run: npm ci
35 - run: npm run build
36 - uses: amondnet/vercel-action@v25
37 with:
38 vercel-token: ${{ secrets.VERCEL_TOKEN }}
39 vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
40 vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
41 vercel-args: '--prod'
Production Performance Tips

Enable Compression

Use Nginx or your hosting platform to enable gzip/brotli compression. Next.js standalone does not compress by default.

CDN for Static Assets

Serve _next/static/ from a CDN with immutable cache headers. These files contain content hashes and can be cached forever.

Database Connection Pooling

Use connection pooling (PgBouncer, Prisma Accelerate, Neon) to handle concurrent database connections efficiently in serverless or multi-instance deployments.

Monitor and Alert

Set up error tracking (Sentry), performance monitoring (Vercel Analytics or custom), and uptime monitoring to catch issues before users do.

โœ“

best practice

Always test your production build locally before deploying. Run npm run build && npm start to catch any build errors or runtime issues in the production configuration.
$Blueprint โ€” Engineering DocumentationยทSection ID: NEXTJS-07ยทRevision: 1.0