Deployment
Build, ship, and monitor. From Vercel one-click to self-hosted on a Node server.
Build for production
npm run build
npm startbuild produces a production bundle and prints a route-by-route summary: which pages are static (●), dynamic (ƒ), or use ISR (◐). Use it to sanity-check your rendering choices before pushing.
Environment variables
- Server-only:
process.env.STRIPE_SECRET— never sent to the browser. - Public (prefixed
NEXT_PUBLIC_): inlined at build time and visible in the browser bundle. - Local dev values go in
.env.local. Never commit secrets.
// .env.local
DATABASE_URL=postgres://...
NEXT_PUBLIC_SITE_URL=https://example.comDeploy on Vercel
Vercel is the smoothest path: connect a Git repo, push to main, get a URL. ISR, image optimization, edge middleware and preview deployments work out of the box. Environment variables go in the project settings.
Self-hosting
Next.js apps are just Node servers. To deploy anywhere:
# build once, then run a long-lived Node process
npm ci
npm run build
NODE_ENV=production node node_modules/next/dist/bin/next start -p 3000Or use the **standalone output** to ship a much smaller server with only the files you need:
// next.config.mjs
export default { output: "standalone" };After next build, the .next/standalone/ folder is a self-contained Node app. Combine it with .next/static and public/ in a container. This is the recommended Docker pattern.
What to check post-deploy
- Open the deployed site and Network panel — confirm pages stream and images serve as AVIF/WebP.
- Hit a 404 path to check
not-found.jsrenders. - If you use ISR or revalidation, manually trigger a revalidate and confirm the page updates.
- Run Lighthouse on the LCP page. The hero image should have
priority.
Try it
You want to deploy in a small Docker image. Add the option to next.config.mjs that produces a standalone server bundle.
Need a hint?
There is a single `output` config option whose value is a string.
Quiz
Pick the best answer. You only get one shot per question.
1. Which env var prefix exposes a value to the browser bundle?
2. What does `output: "standalone"` produce?
3. After `npm run build`, where do you confirm which pages are static vs. dynamic?