# Deploying a Node.js Backend in Production with PM2 & Nginx
Deploying a Node.js backend is easy until you do it on a real VPS server.
Locally, everything works perfectly:
npm start
The API launches, connects to the database, Postman returns the correct response, and everything looks good.
But once you move your project to a production server, you'll quickly face questions like:
- What happens if the server reboots?
- Will the application restart automatically?
- Should users access port
5000directly? - How do you configure HTTPS?
- Where do logs go?
- How should you restart the application after deployment?
These are common mistakes during a first production deployment.
Many beginners simply run:
node server.js
While this works for testing, it is not suitable for production.
Problems with this approach:
- The process may stop when your SSH session ends.
- If the app crashes, it won't restart automatically.
- After a server reboot, your application stays offline.
Instead, a proper production setup uses:
- PM2 — Process manager for Node.js
- Nginx — Reverse proxy and web server
Production Architecture
Internet
│
▼
Nginx (80 / 443)
│
▼
Node.js App (localhost:5000)
│
▼
PM2 Process Manager
Important: Users should never communicate directly with your Node.js application.
Instead of exposing:
http://your-server-ip:5000
Users should access:
https://your-domain.com
Nginx receives the request, handles HTTP/HTTPS, and forwards it to your backend.
Step 1 — Connect to Your Server
If using the root user:
ssh root@your-server-ip
Or if using Ubuntu:
ssh ubuntu@your-server-ip
Replace your-server-ip with your VPS IP address.
Step 2 — Update Your Server
Always update the package list first.
sudo apt update && sudo apt upgrade -y
Step 3 — Install Node.js
For Node.js v20:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install nodejs -y
Verify the installation:
node -v
npm -v
Step 4 — Clone Your Backend Project
Clone your repository:
git clone https://github.com/your-username/your-backend-repo.git
Move into the project:
cd your-backend-repo
Install dependencies:
npm install
If your application uses environment variables:
nano .env
Example:
PORT=5000
DATABASE_URL=your_database_url
JWT_SECRET=your_secret_key
NODE_ENV=production
Never commit your .env file.
.gitignore
.env
Step 5 — Test the Application
If you have a start script:
npm start
Otherwise:
node server.js
This verifies that:
- Environment variables are correct
- Database connection works
- Dependencies are installed
- Production configuration is valid
Stop the application afterward:
CTRL + C
Step 6 — Install PM2
Install PM2 globally:
sudo npm install pm2 -g
Start your application.
Using server.js:
pm2 start server.js --name backend-api
Or using the npm start script:
pm2 start npm --name backend-api -- start
Verify:
pm2 list
Step 7 — Enable Auto Startup
Generate the startup script:
pm2 startup
PM2 will display another command.
Run the generated command (it begins with sudo).
Finally save the process list:
pm2 save
Now your application starts automatically after server reboots.
Step 8 — Install Nginx
Install Nginx:
sudo apt install nginx -y
Start the service:
sudo systemctl start nginx
Enable startup:
sudo systemctl enable nginx
Check status:
sudo systemctl status nginx
Step 9 — Configure Nginx
Create a configuration file:
sudo nano /etc/nginx/sites-available/backend
Paste the following:
server {
listen 80;
server_name your-domain.com www.your-domain.com;
location / {
proxy_pass http://localhost:5000;
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;
}
}
Replace your-domain.com with your real domain.
Enable the configuration:
sudo ln -s /etc/nginx/sites-available/backend /etc/nginx/sites-enabled/
Test the configuration:
sudo nginx -t
Restart Nginx:
sudo systemctl restart nginx
Step 10 — Configure the Firewall
If you're using UFW:
Allow SSH:
sudo ufw allow OpenSSH
Allow HTTP & HTTPS:
sudo ufw allow 'Nginx Full'
Enable the firewall:
sudo ufw enable
Only these ports should be accessible:
- SSH
- HTTP (80)
- HTTPS (443)
Do not expose port 5000.
Step 11 — Configure HTTPS with Certbot
Install Certbot:
sudo apt install certbot python3-certbot-nginx -y
Generate the SSL certificate:
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
Certbot automatically:
- Creates SSL certificates
- Updates your Nginx configuration
- Redirects HTTP → HTTPS
Useful PM2 Commands
Check running applications
pm2 list
Restart application
pm2 restart backend-api
Stop application
pm2 stop backend-api
Delete application
pm2 delete backend-api
View logs
pm2 logs
Open monitoring dashboard
pm2 monit
Save current process list
pm2 save
The most useful command during debugging:
pm2 logs
Typical Deployment Workflow
Once everything is configured, future deployments become simple.
cd your-backend-repo
git pull
npm install
pm2 restart backend-api
Why Use PM2 & Nginx?
Instead of exposing your Node.js server directly, using PM2 and Nginx provides several advantages:
- Automatic application restarts
- Auto-start after server reboot
- SSL/TLS support
- Reverse proxy functionality
- Better logging
- Easier scaling
- Multiple applications or subdomains on one server
- Improved security
Production Best Practices
Use environment variables
Good:
process.env.JWT_SECRET
Bad:
const jwtSecret = "your_secret_here";
Only expose necessary ports
Open only:
- 22 (SSH)
- 80 (HTTP)
- 443 (HTTPS)
Avoid exposing your application port (5000).
Always monitor logs
pm2 logs
Restart after deployment
pm2 restart backend-api
Summary
Production deployment is much more than simply starting your Node.js application.
A production-ready backend should:
- ✅ Automatically restart after crashes
- ✅ Start after server reboots
- ✅ Use HTTPS
- ✅ Hide internal application ports
- ✅ Generate accessible logs
- ✅ Be easy to update and maintain
Using PM2 together with Nginx provides a reliable, secure, and scalable setup for deploying Node.js applications on any Ubuntu-based VPS, whether it's AWS EC2, DigitalOcean, Hetzner, Azure, Linode, Contabo, or similar cloud providers.
Top comments (0)