Self Hosting

Next.js does not need to run on a platform like Vercel. It can run as a long-lived Node.js process on any server or VM you control, started with the framework's own CLI. This trades away some of the automatic global infrastructure Vercel provides in exchange for full control over the environment -- useful when the app needs to live inside an existing data center, an internal network, or a specific compliance boundary.

Building and Starting

next build compiles the application; next start then boots a Node.js server that serves it, handling static assets, prerendered pages, on-demand ISR regeneration, Server Actions, and Route Handlers the same way Vercel's infrastructure would, just from a single process rather than a distributed edge network.


terminal:
npm run build   # runs "next build"
npm start        # runs "next start", listens on port 3000 by default

# override the port and hostname if needed
next start -p 4000 -H 0.0.0.0

Because next start keeps a Node.js process running, it needs a process manager (pm2, systemd, or your container orchestrator's own restart policy) to keep it alive across crashes and server reboots -- unlike a serverless platform, nothing restarts it for you.



The output: 'standalone' Option

By default, next build assumes the final deployment will have access to the project's full node_modules folder, which for most apps is far larger than what's actually needed at runtime. Setting output: 'standalone' in next.config.js makes the build trace every file each page and Route Handler actually imports and copy only that minimal subset -- plus a small server.js entry point -- into .next/standalone. The result is a self-contained folder that can be deployed on its own, without running npm install on the target machine at all.

The public folder and .next/static are deliberately left out of the trace and must be copied next to the standalone output by hand (or by your build script) before starting it.

next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
}

module.exports = nextConfig

terminal:
next build

cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/static

node .next/standalone/server.js


What You Lose Compared to Vercel

Middleware, Server Actions, ISR, and the Image Optimization endpoint all still work under next start -- none of them are Vercel-exclusive. What's missing is the infrastructure Vercel wraps around them:

For production image handling, installing the optional sharp package is strongly recommended -- Next.js uses it for on-server image optimization and falls back to a much slower, unoptimized path without it.


terminal:
npm install sharp


Reverse-Proxying with nginx

In production, next start is rarely exposed to the internet directly. It's more common to put nginx (or another reverse proxy) in front, terminating TLS, serving .next/static assets straight off disk with long-lived cache headers, and forwarding everything else to the Node.js process.


nginx.conf:
server {
  listen 80;
  server_name example.com;

  # hashed, immutable build assets -- safe to cache aggressively
  location /_next/static/ {
    alias /var/www/myapp/.next/static/;
    expires 365d;
    access_log off;
  }

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_cache_bypass $http_upgrade;
  }
}

The most common way to package a self-hosted build like this for repeatable deployments is to wrap it in a container -- see Docker. If the app doesn't need server-side rendering at all, Static Export removes the Node.js server from the picture entirely.