Deploying a Next.js application on a VPS sounds complicated at first. But once you understand the structure, it becomes a clean and repeatable process.
In this guide, we’ll deploy a production-ready Next.js app on an Ubuntu server using Nginx as a reverse proxy and PM2 as a process manager. This setup is stable, scalable, and widely used in real-world production environments.
What We Are Setting Up
- Ubuntu VPS (20.04 or 22.04 recommended)
- Node.js installed
- Next.js production build running on a port
- PM2 managing the Node process
- Nginx reverse proxy forwarding traffic to the app
This structure ensures your app runs continuously and automatically restarts if it crashes.
Step 1: Connect to Your Ubuntu Server
Use SSH to access your server:
ssh username@your_server_ip
Update system packages:
sudo apt update
sudo apt upgrade -y
Step 2: Install Node.js
Install Node.js using NodeSource:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
Verify installation:
node -v
npm -v
Step 3: Upload Your Next.js Project
You can either clone from GitHub:
git clone https://github.com/your-repo/project.git
cd project
Or upload files manually using SFTP.
Install dependencies:
npm install
Step 4: Create Production Build
npm run build
Test locally on server:
npm start
By default, it runs on port 3000.
Step 5: Install and Configure PM2
Install PM2 globally:
sudo npm install -g pm2
Start your app with PM2:
pm2 start npm --name "nextjs-app" -- start
Check running processes:
pm2 list
Enable auto start on reboot:
pm2 startup
pm2 save
Now your app will restart automatically if the server reboots.
Step 6: Install Nginx
sudo apt install nginx -y
Allow firewall:
sudo ufw allow 'Nginx Full'
Step 7: Configure Nginx as Reverse Proxy
Create a new config file:
sudo nano /etc/nginx/sites-available/yourdomain
Add this configuration:
server {
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Enable the config:
sudo ln -s /etc/nginx/sites-available/yourdomain /etc/nginx/sites-enabled/
Test Nginx:
sudo nginx -t
Restart Nginx:
sudo systemctl restart nginx
Step 8: Point Domain to Server
Update your domain’s DNS A record to point to your VPS IP address. Once propagated, your domain should load the Next.js application.
Production Best Practices
Use Environment Variables
Store sensitive values in a .env file and never commit it to GitHub.
Enable SSL
Use Certbot to generate a free SSL certificate for HTTPS.
Enable Gzip in Nginx
Improves performance by compressing responses.
Monitor Logs
pm2 logs
Check Nginx logs if needed:
sudo tail -f /var/log/nginx/error.log
Common Deployment Mistakes
- Forgetting to run npm run build
- Wrong port in Nginx proxy_pass
- DNS not properly configured
- Firewall blocking traffic
Final Thoughts
This deployment structure is simple, scalable, and reliable. Nginx handles incoming traffic efficiently, while PM2 ensures your Node process stays alive.
Once you understand this flow, deploying any future Next.js application becomes straightforward. Focus on clean builds, proper environment handling, and server security for long-term stability.
