Next.js Deployment
Deploy Next.js apps to production on Vercel, Docker, and other platforms.
TL;DR
- 01Run npm run build to compile your app for production.
- 02Deploy to Vercel with one command or by connecting a Git repo.
- 03Set environment variables in your deployment platform dashboard.
Tips
- 01Use Vercel for simplest deployment — it's optimized for Next.js and includes preview deployments.
Warnings
- 01Never commit .env.production files — use your deployment platform's secret management.
Building for Production
- Build your app for production.
npm run build npm start - Build creates .next folder with optimized files.
- Test production build locally before deploying.
npm run build npm start # Visit http://localhost:3000 - Inspect the build output for page sizes and bundle weights.
npm run build # Route Size First Load JS # / 5 kB 87 kB # /blog/[slug] 3 kB 85 kB - Use output: 'standalone' to create a minimal deployment folder.
// next.config.js module.exports = { output: "standalone" // .next/standalone contains everything needed to run }; - Set NODE_ENV=production when running the server manually.
NODE_ENV=production node .next/standalone/server.js
Vercel Deployment
- Deploy to Vercel (easiest option).
npm install -g vercel vercel - Connect Git repository for automatic deployments.
git push # Automatically deploys to Vercel - Vercel is created by Next.js team.
- Preview deployments are created automatically for every pull request.
- Set environment variables using the Vercel CLI.
vercel env add DATABASE_URL production vercel env add NEXT_PUBLIC_API_URL production - Override the build command in vercel.json if needed.
{ "buildCommand": "npm run build", "outputDirectory": ".next" }
Docker Deployment
- Create Dockerfile for Docker deployment.
FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build EXPOSE 3000 CMD ["npm", "start"] - Build and run Docker image.
docker build -t my-app . docker run -p 3000:3000 my-app - Use multi-stage builds to reduce the final image size.
FROM node:18-alpine AS builder WORKDIR /app COPY . . RUN npm ci && npm run build FROM node:18-alpine AS runner COPY --from=builder /app/.next/standalone ./ CMD ["node", "server.js"] - Pass environment variables at container runtime.
docker run -p 3000:3000 -e DATABASE_URL=postgres://... my-app - Use docker-compose for local development with a database.
services: app: build: . ports: ["3000:3000"] db: image: postgres:15 environment: POSTGRES_PASSWORD: secret
Environment Variables
- Set environment variables in deployment platform.
# Vercel dashboard vercel env add DATABASE_URL vercel env add NEXT_PUBLIC_API_URL - Use .env.production for local production testing.
# .env.production DATABASE_URL=postgresql://prod-db NEXT_PUBLIC_API_URL=https://api.prod.com - Prefix public variables with NEXT_PUBLIC_ to expose to the browser.
NEXT_PUBLIC_STRIPE_KEY=pk_live_abc123 DATABASE_URL=postgres://secret # server-only - Validate required env variables at startup to catch missing config early.
// lib/env.ts if (!process.env.DATABASE_URL) { throw new Error("DATABASE_URL is required"); } - Store secrets in your CI/CD platform and never commit them to Git.
# GitHub Actions env: DATABASE_URL: ${{ secrets.DATABASE_URL }}
Performance Optimization
- Enable Image Optimization for faster images.
import Image from 'next/image'; <Image src="/photo.jpg" alt="Photo" width={800} height={600} priority /> - Use static exports for fully static sites with no server needed.
// next.config.js module.exports = { output: "export" // Creates an out/ folder you can host on any CDN }; - Enable compression for smaller HTTP responses.
module.exports = { compress: true // enabled by default in production }; - Configure CDN caching for static assets.
module.exports = { headers: async () => [ { source: "/_next/static/(.*)", headers: [{ key: "Cache-Control", value: "public, max-age=31536000, immutable" }] } ] }; - Run lighthouse audits against the deployed production URL.
npx lighthouse https://your-app.vercel.app --output html
FAQ
Install the Vercel CLI and run vercel from your project root — it auto-detects Next.js and configures everything. Alternatively, connect your GitHub repo in the Vercel dashboard and every push deploys automatically with zero config.
It tells Next.js to produce a self-contained build folder with only the files needed to run, which is essential for Docker deployments since it dramatically reduces image size. Use it whenever you're containerizing your app rather than deploying to a managed platform.
Variables prefixed with NEXT_PUBLIC_ are inlined into the client-side JavaScript bundle at build time and are visible in the browser. Variables without that prefix are server-only and accessible exclusively in API routes, Server Components, and data-fetching functions like getServerSideProps.
The most common cause is missing environment variables — your .env.local file isn't deployed, so any variables it defines must be explicitly set in your platform's dashboard. Check that all variables your app reads at runtime are registered in the deployment environment, not just at build time.
Switch data-heavy pages from getServerSideProps to Incremental Static Regeneration (ISR) using revalidate so responses are served from the CDN cache instead of computed on every request. Also enable compress: true in next.config.js if your hosting platform doesn't handle gzip automatically.