Deploying a Next.js application to your own Virtual Private Server (VPS) offers complete control over resources, predictable hosting costs, and zero platform lock-in. While serverless platforms like Vercel are great for quick prototypes, running Next.js on an Ubuntu VPS backed by PM2 and Nginx Reverse Proxy is the standard architecture for high-performance, cost-effective production deployments.
In this tutorial, you will learn step-by-step how to set up an Ubuntu server from scratch, build your Next.js application, daemonize it with PM2, route web traffic securely using Nginx, and install a free Let's Encrypt SSL certificate.
---
Prerequisites
Before starting, make sure you have:
* An Ubuntu 22.04 or 24.04 LTS VPS (from providers like UltaHost, DigitalOcean, or Linode) with root or sudo access.
* A registered domain name pointed to your VPS IP address (create an `A` record pointing `@` and `www` to your server's Public IP).
* A Next.js codebase hosted on GitHub or GitLab.
---
Step 1: Update Server and Install System Dependencies
SSH into your server:
ssh root@your_server_ipUpdate package repositories and install essential build tools:
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git ufw build-essential---
Step 2: Install Node.js via NodeSource
Next.js requires modern LTS Node.js. Install Node.js 20 or 22 via NodeSource:
curl -fsSL [https://deb.nodesource.com/setup_20.x](https://deb.nodesource.com/setup_20.x) | sudo -E bash -
sudo apt install -y nodejsVerify that Node and npm are installed successfully:
node -v
npm -v---
Step 3: Clone and Build the Next.js Project
A good practice is to host your web apps under `/var/www/`:
sudo mkdir -p /var/www
cd /var/www
sudo git clone [https://github.com/your-username/your-nextjs-repo.git](https://github.com/your-username/your-nextjs-repo.git) my-app
cd my-appCreate your production environment file:
nano .env.production*(Add your production secrets and API keys, then press `CTRL + O`, `Enter`, and `CTRL + X` to save).*
Install dependencies and generate the production build:
npm install
npm run build---
Step 4: Manage the Next.js App with PM2
Running `npm start` directly in the terminal will stop the app as soon as your SSH session closes. PM2 is an enterprise process manager that keeps your app running 24/7 and restarts it automatically after server reboots or fatal crashes.
Install PM2 globally:
sudo npm install -g pm2Start your Next.js application with PM2:
pm2 start npm --name "my-app" -- start*(Optional)* If you want to use a custom port (e.g., port 3000 or 8080), create an `ecosystem.config.js` in your root directory:
module.exports = {
apps: [
{
name: 'my-app',
script: 'node_modules/next/dist/bin/next',
args: 'start -p 3000',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
},
},
],
};Then start with:
pm2 start ecosystem.config.jsConfigure PM2 to persist across system reboots:
pm2 save
pm2 startup*(Copy and run the `sudo env PATH=...` command generated in your terminal output).*
---
Step 5: Install and Configure Nginx as Reverse Proxy
Nginx receives external HTTPS traffic on port 80/443 and securely forwards requests to your internal Next.js application listening on port 3000.
Install Nginx:
sudo apt install -y nginxCreate a dedicated Nginx server block configuration for your domain:
sudo nano /etc/nginx/sites-available/yourdomain.comPaste the following optimized configuration (replace `yourdomain.com` with your actual domain):
server {
listen 80;
server_name yourdomain.com [www.yourdomain.com](https://www.yourdomain.com);
# Gzip Compression for fast delivery
gzip on;
gzip_proxied any;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
location / {
proxy_pass [http://127.0.0.1:3000](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_cache_bypass $http_upgrade;
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;
}
}Enable the configuration by creating a symlink:
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/Test Nginx configuration syntax:
sudo nginx -tIf the test is successful, reload Nginx:
sudo systemctl reload nginx---
Step 6: Secure Your Server with UFW Firewall & Free SSL
Configure Ubuntu's uncomplicated firewall (UFW) to allow SSH and web traffic:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enableNext, install Certbot to generate a free Let's Encrypt SSL certificate:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d [www.yourdomain.com](https://www.yourdomain.com)Follow the interactive prompts. Certbot will automatically issue the certificate, configure HTTPS redirect rules inside your Nginx configuration, and set up an automatic renewal cron job.
---
Step 7: Zero-Downtime Deployment Workflow
Whenever you push updates to GitHub, update your live app with this simple deployment sequence:
cd /var/www/my-app
git pull origin main
npm install
npm run build
pm2 reload my-appUsing `pm2 reload` instead of `pm2 restart` performs a graceful reload with zero downtime for incoming visitors.
---
Conclusion
Your Next.js application is now fully deployed on your Ubuntu VPS with production-grade stability, automated process monitoring via PM2, high-throughput caching via Nginx, and complete HTTPS encryption.
Looking for an affordable, high-performance NVMe VPS to deploy your Next.js projects? Check out UltaHost VPS Plans starting at just $4.50/month with zero renewal price shocks.