How to Deploy a Next.js App to a VPS
Build, run, and serve a production Next.js app on Ubuntu 24.04: PM2 process management, an Nginx reverse proxy, and free HTTPS.

You do not need Vercel to run Next.js. A modest Ubuntu 24.04 VPS runs a production Next.js app comfortably, with full control over cost, environment, and data. This guide takes you from source code to a live HTTPS site using PM2 and Nginx.
What you will build
The flow is: Node runs the Next.js production server on a local port, PM2 keeps it alive, and Nginx terminates TLS and proxies traffic to it. First, make sure Node is installed — installing Node.js with NVM is the cleanest way.
Get your code onto the server
Clone your repository (or set up automated pushes with git-based deploys):
cd ~
git clone https://github.com/you/your-next-app.git app
cd app
npm install
Build for production
Next.js must be compiled before it can run in production mode:
npm run build
This creates the optimised .next build. Test the production server:
npm run start
By default it listens on port 3000. Confirm with curl http://127.0.0.1:3000 from another session, then stop it with Ctrl+C — PM2 will run it properly.

Keep it running with PM2
Install PM2 and start the Next.js server through it. The cleanest way is to point PM2 at the npm start script:
npm install -g pm2
pm2 start "npm run start" --name nextapp
pm2 save
pm2 startup
Run the sudo command pm2 startup prints so the app survives reboots. Our dedicated guide to deploying Node apps with PM2 covers logs, reloads, and clustering in more depth.
Set environment variables
Production apps need secrets and config. Create a .env.production file (or .env.local) in the project root with your variables — database URLs, API keys, NODE_ENV=production. Keep it out of git. Rebuild after changing any NEXT_PUBLIC_ variable, since those are baked in at build time.
Put Nginx in front
Create /etc/nginx/sites-available/app.example.com:
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Enable and reload:
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
The Upgrade/Connection headers matter because Next.js uses WebSockets for features like fast refresh and some server actions. The full detail is in our Nginx reverse proxy guide.
Add HTTPS
Finish with a free certificate:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d app.example.com
Certbot rewrites the server block for TLS and sets up auto-renewal — see the Certbot guide for details.
Trim the production footprint
Next.js can emit a self-contained standalone build that bundles only the files needed to run, which starts faster and uses less disk. In next.config.js:
module.exports = { output: 'standalone' };
After npm run build, the .next/standalone folder contains a minimal server you can run with node .next/standalone/server.js — especially handy if you later containerise the app. A few production reminders that save headaches: set NODE_ENV=production so Next.js uses optimised code paths; run the build on a machine with enough RAM, since compiling is the memory-hungry part; and remember NEXT_PUBLIC_ variables are inlined at build time, so changing one means rebuilding, not just restarting.
Deploying updates
To ship a new version:
git pull
npm install
npm run build
pm2 reload nextapp
FAQ
Do I need a Node server, or can I export a static site?
If your app uses SSR, API routes, or server actions, you need the Node server as above. If it is fully static, next build with output: 'export' produces plain files you can serve directly — see hosting a static website with Nginx.
How much RAM does a Next.js app need on a VPS?
The build step is the memory-hungry part; 1–2 GB is fine for small-to-medium apps, more for large ones. If builds get killed, they ran out of memory — build locally or in CI and deploy the .next output.
Why does my app work locally but 502 on the VPS?
A 502 from Nginx means the upstream is not answering. Check pm2 status and pm2 logs nextapp — the app probably crashed or is on a different port than your proxy_pass.
Can I run several Next.js apps on one VPS?
Yes. Give each its own port, its own PM2 name, and its own Nginx server block with a different server_name. See running staging and production on one VPS.
Run Next.js your way on Nxeon Node.js hosting or a developer VPS — full root, NVMe SSDs, and free migration help to lift an app off a pricier platform.