Deploy Next.js apps to production on Vercel, Docker, and other platforms.
npm run build
npm startnpm run build
npm start
# Visit http://localhost:3000npm run build
# Route Size First Load JS
# / 5 kB 87 kB
# /blog/[slug] 3 kB 85 kB// next.config.js
module.exports = {
output: "standalone"
// .next/standalone contains everything needed to run
};NODE_ENV=production node .next/standalone/server.jsnpm install -g vercel
vercelgit push # Automatically deploys to Vercelvercel env add DATABASE_URL production
vercel env add NEXT_PUBLIC_API_URL production{
"buildCommand": "npm run build",
"outputDirectory": ".next"
}FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]docker build -t my-app .
docker run -p 3000:3000 my-appFROM 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"]docker run -p 3000:3000 -e DATABASE_URL=postgres://... my-appservices:
app:
build: .
ports: ["3000:3000"]
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret# Vercel dashboard
vercel env add DATABASE_URL
vercel env add NEXT_PUBLIC_API_URL# .env.production
DATABASE_URL=postgresql://prod-db
NEXT_PUBLIC_API_URL=https://api.prod.comNEXT_PUBLIC_STRIPE_KEY=pk_live_abc123
DATABASE_URL=postgres://secret # server-only// lib/env.ts
if (!process.env.DATABASE_URL) {
throw new Error("DATABASE_URL is required");
}# GitHub Actions
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}import Image from 'next/image';
<Image
src="/photo.jpg"
alt="Photo"
width={800}
height={600}
priority
/>// next.config.js
module.exports = {
output: "export"
// Creates an out/ folder you can host on any CDN
};module.exports = {
compress: true // enabled by default in production
};module.exports = {
headers: async () => [
{
source: "/_next/static/(.*)",
headers: [{ key: "Cache-Control", value: "public, max-age=31536000, immutable" }]
}
]
};npx lighthouse https://your-app.vercel.app --output htmlInstall 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.