Building a Next.js application is one thing. Making it available to the world is another. Deployment is the process of taking the production build — the output of npm run build — and placing it on infrastructure that can serve it to users at scale, with acceptable performance, security, and reliability.
Next.js is flexible in how it can be deployed. Because it is built on Node.js and outputs standard files, it runs on any platform capable of serving Node.js applications, static files, or both. The choice of platform is a practical decision shaped by the application's requirements, the team's operational preferences, and the features the deployment target supports.
What the Production Build Contains
Running npm run build analyses the application and produces an optimised output in the .next/ directory. Understanding what this output contains is useful context for every deployment decision that follows.
npm run build
The output includes:
- Pre-rendered HTML files for every statically generated route
- JavaScript bundles — split per route, minified, and tree-shaken
- Server functions for dynamically rendered routes and Route Handlers
- A build manifest mapping routes to their assets and rendering strategy
- Optimised image and font configurations
The build also reports the rendering strategy for every route — static, ISR, or dynamic — which is a useful audit of where server rendering is happening and whether any routes are unexpectedly dynamic.
Route (app) Size First Load JS
┌ ○ / 4.2 kB 89.4 kB
├ ○ /about 1.1 kB 86.3 kB
├ ● /blog/[slug] 2.3 kB 87.5 kB
└ ƒ /dashboard 3.8 kB 88.9 kB
○ Static ● ISR ƒ Dynamic
Any route marked ƒ is rendered per request on the server. Routes marked ○ or ● are pre-built and served from the cache. Reviewing this output before deploying is a good practice — an unexpected ƒ on a route that should be static often indicates an unintentional dynamic function call, such as reading cookies() in a component that does not need it.
Deploying to Vercel
Vercel is the platform built by the same team that builds Next.js. It is the path of least resistance for most Next.js applications and the deployment target with the deepest framework integration.
Connecting a Repository
Deploying to Vercel from a GitHub repository takes a small number of steps:
- Push the Next.js project to a GitHub repository
- Sign in to vercel.com and click Add New Project
- Import the repository — Vercel detects Next.js automatically and configures the build settings
- Add environment variables under Project Settings → Environment Variables
- Click Deploy
From this point, every push to the main branch triggers a new production deployment. Every pull request gets its own preview deployment — a unique URL where the changes can be reviewed before merging. Preview deployments are one of Vercel's most practically useful features: stakeholders can review changes on a live URL without the deployment touching production.
What Vercel Provides
Beyond running the build and serving the output, Vercel handles the surrounding infrastructure automatically:
SSL certificates — HTTPS is enabled by default, with certificates managed and renewed automatically.
Global CDN — static assets and pre-rendered pages are distributed to edge nodes worldwide, served from the location closest to the user.
Serverless functions — dynamic routes and Route Handlers are deployed as serverless functions, scaling to zero when idle and scaling up automatically under load. There is no server to manage or right-size.
Edge Middleware — Next.js middleware runs at the edge, close to the user, before the request reaches the application. Useful for authentication redirects, geolocation-based routing, and A/B testing.
Incremental Static Regeneration — ISR is natively supported and works without any additional configuration on Vercel's infrastructure.
Analytics and monitoring — Vercel provides built-in Web Vitals monitoring and deployment analytics.
Custom Domains
Adding a custom domain to a Vercel project is done through the project dashboard under Settings → Domains. Vercel provides the DNS records to configure at the domain registrar. Once configured, the custom domain is served with HTTPS automatically.
Deploying to Other Platforms
Vercel is not the only option. Next.js runs on any Node.js-capable hosting environment, and several platforms provide well-integrated Next.js support.
Self-Hosted Node.js Server
The most portable deployment option is running Next.js as a standard Node.js server. The build output includes a standalone server that handles both static and dynamic routes.
Enabling the standalone output in next.config.ts:
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'standalone',
};
export default nextConfig;
With output: 'standalone', Next.js produces a self-contained .next/standalone directory that includes only the files needed to run the application — the server, the page chunks, and their dependencies — without the full node_modules tree. This significantly reduces the deployment artefact size.
Starting the server:
node .next/standalone/server.js
The server listens on port 3000 by default. In production, it is typically placed behind a reverse proxy — Nginx or Caddy — which handles SSL termination, compression, and serves the static assets from .next/static directly.
This approach gives complete control over the hosting environment and is appropriate for teams that manage their own infrastructure — a VPS on DigitalOcean, an EC2 instance on AWS, an on-premises server.
Railway and Render
Railway and Render are managed platforms that run persistent Node.js servers — the right environment for Next.js's dynamic rendering capabilities, Edge Middleware, and ISR, which require an always-on process rather than purely serverless execution.
Deploying to Railway or Render follows a similar pattern to Vercel:
- Connect the GitHub repository
- The platform detects Next.js and configures the build command (
npm run build) and start command (npm start) automatically - Set environment variables in the platform dashboard
- Deploy
Both platforms provide custom domain support with automatic SSL, persistent logs, and environment-based deployments. Railway in particular has become a popular choice for Next.js deployments that need a persistent server with predictable pricing — Vercel's serverless model prices per function invocation, which can become expensive under sustained high traffic.
Static Export
For Next.js applications that use only static rendering — no server-side rendering, no Route Handlers, no dynamic features — the application can be exported as a set of plain HTML, CSS, and JavaScript files, which can be hosted anywhere that serves static files: an S3 bucket, Cloudflare Pages, GitHub Pages, or any CDN.
Enabling static export:
// next.config.ts
const nextConfig: NextConfig = {
output: 'export',
};
Running npm run build produces the static output in the out/ directory. Every page is a .html file, fully rendered, requiring no server to serve.
The limitation is significant: static export disables everything that requires a running server — dynamic routes without generateStaticParams, Route Handlers, ISR, Middleware, and image optimisation through next/image. For applications that use any of these features, static export is not an option. For applications that genuinely do not — a marketing site, a documentation portal, a blog with no interactive backend — it is the simplest and cheapest deployment path available.
Environment Variables in Production
Environment variables configured locally in .env.local are never deployed — they live only on the development machine. Production environment variables must be configured in the deployment platform separately.
On Vercel, this is done through the project dashboard under Settings → Environment Variables. Variables can be scoped to production, preview, or development environments individually — a database URL pointing to the production database can be configured to apply only to production deployments, while preview deployments use a separate staging database.
The NEXT_PUBLIC_ prefix rule applies equally in production: variables that do not carry this prefix are server-only and are never included in the browser bundle. Secrets — API keys, database connection strings, authentication secrets — should never carry the NEXT_PUBLIC_ prefix, regardless of environment.
A common and important practice is to validate required environment variables at startup, as covered in the Environment Variables topic of the Node.js series:
// lib/env.ts — called at the top of the application entry point
const requiredEnvVars = [
'DATABASE_URL',
'NEXTAUTH_SECRET',
'NEXTAUTH_URL',
] as const;
for (const key of requiredEnvVars) {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
}
A deployment that fails at startup due to a missing environment variable is far preferable to one that starts successfully and then fails unpredictably at runtime when the missing variable is finally used.
Deployment Checklist
Before pushing to production, a set of consistent checks reduces the probability of a broken or underperforming deployment.
Build locally first. Running npm run build locally catches type errors, missing environment variables at build time, and configuration issues before they surface in the deployment pipeline. A build that fails locally is faster to diagnose than one that fails in a CI pipeline.
Review the build output. Check the route table for unexpected dynamic routes — any ƒ that should be ○ or ● is worth investigating before deploying.
Verify environment variables. Confirm that every required variable is configured in the deployment platform for the target environment. Missing secrets are one of the most common causes of production failures.
Test the production build locally. Running npm run build && npm run start before deploying is the closest approximation to the production environment available without actually deploying. Issues that only appear in production — subtle differences between development and production rendering, missing static files — are caught here.
Check the next.config.ts output setting. Confirm that the output option matches the deployment target. 'standalone' for a Node.js server deployment, 'export' for static hosting, and nothing (the default) for Vercel.
After Deployment
Monitoring
A deployed application without monitoring is an application where problems go undetected until users report them. Basic monitoring covers two concerns: uptime — is the application responding to requests? — and errors — when things go wrong, where and why?
Vercel provides Web Vitals monitoring built in. For other platforms, services like Sentry capture uncaught exceptions and unhandled Promise rejections automatically, aggregate them by frequency and impact, and provide the stack trace and context needed to reproduce and fix them. This connects directly to the error handling principles established in the Node.js and Next.js error state topics — production debugging relies on captured context, not on reproducing the issue live.
Continuous Deployment
The most practical deployment workflow for most teams is continuous deployment — every merge to the main branch triggers a production deployment automatically, with no manual steps. Vercel does this natively. For self-hosted or other-platform deployments, a GitHub Actions workflow handles it:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build
- run: npm run start & sleep 5 && curl -f http://localhost:3000
# platform-specific deploy step follows
The pattern — install, build, verify, deploy — is the same regardless of the target platform. The build step failing in CI before the deploy step is reached is the correct behaviour; it means a broken build does not reach production.
Choosing a Deployment Platform
| Vercel | Railway / Render | Self-hosted | |
|---|---|---|---|
| Setup complexity | Minimal | Low | High |
| Next.js feature support | Complete | Complete | Complete (with configuration) |
| ISR support | Native | Yes | Yes |
| Pricing model | Per invocation (serverless) | Flat monthly | Infrastructure cost |
| Persistent server | No (serverless) | Yes | Yes |
| Custom infrastructure control | Low | Medium | Full |
| Best for | Most Next.js applications | High-traffic apps, predictable billing | Teams managing own infrastructure |
For a new Next.js project without specific infrastructure constraints, Vercel is the practical starting point — it requires the least configuration, supports every Next.js feature natively, and deploys in minutes. The option to migrate to another platform is always available, and doing so is straightforward given the portability of the standalone output.