Deploy Node.js App: PM2 and Nginx Guide
Complete guide to deploying Node.js applications on a VPS. Node.js installation with nvm, PM2 process manager, Nginx reverse proxy, SSL certificate, and environment variables management.
Table of Contents
Deploy Node.js Application: PM2 and Nginx Guide
Deploying Node.js applications to a production environment is a systematic process with the right tools and configuration. This guide covers everything from Node.js installation with nvm to PM2 process manager, Nginx reverse proxy configuration, and SSL certificate setup.
Node.js Installation (with nvm)
nvm (Node Version Manager) lets you manage multiple Node.js versions and is recommended for production environments.
# Download and run the nvm install script
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Reload shell
source ~/.bashrc
# Check nvm version
nvm --version
# Install latest LTS version
nvm install --lts
# Install specific version
nvm install 20.11.0
# Set default version
nvm use --lts
nvm alias default node
# Check Node.js and npm versions
node --version
npm --version
Always use the LTS (Long Term Support) version in production. LTS versions receive security updates for a longer period.
Transferring the Application to the Server
Cloning with Git
# Create application directory
sudo mkdir -p /var/www/myapp
sudo chown $USER:$USER /var/www/myapp
# Clone the repo
cd /var/www
git clone https://github.com/user/myapp.git
cd myapp
# Install dependencies (production)
npm install --production
# Or install all dependencies
npm install
Environment Variables (.env)
# Create .env file
nano /var/www/myapp/.env
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
JWT_SECRET=your-secret-key-here
REDIS_URL=redis://localhost:6379
# Secure the .env file
chmod 600 /var/www/myapp/.env
Never add the .env file to your Git repository. Add .env to your .gitignore file.
PM2 Process Manager
PM2 is the most popular tool for managing Node.js applications in production. It offers automatic restarts, log management, and cluster mode.
Installing PM2
# Install PM2 globally
npm install -g pm2
# Check PM2 version
pm2 --version
Starting the Application with PM2
# Simple start
pm2 start app.js --name myapp
# With specific port
pm2 start app.js --name myapp -- --port 3000
# Using npm start
pm2 start npm --name myapp -- start
# Cluster mode (instances equal to CPU cores)
pm2 start app.js --name myapp -i max
# Specific number of instances
pm2 start app.js --name myapp -i 4
PM2 Ecosystem File
Use an ecosystem.config.js file for more advanced configuration:
nano /var/www/myapp/ecosystem.config.js
module.exports = {
apps: [
{
name: 'myapp',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 3000
},
error_file: '/var/log/pm2/myapp-error.log',
out_file: '/var/log/pm2/myapp-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
max_memory_restart: '500M',
watch: false,
autorestart: true
}
]
};
# Start with ecosystem file
pm2 start ecosystem.config.js --env production
# PM2 basic commands
pm2 list # List running apps
pm2 status # Status summary
pm2 logs myapp # View logs
pm2 logs myapp --lines 100 # Last 100 lines of logs
pm2 restart myapp # Restart
pm2 reload myapp # Zero-downtime reload
pm2 stop myapp # Stop
pm2 delete myapp # Delete
pm2 monit # Real-time monitoring
PM2 Auto-start on System Boot
# Generate startup script
pm2 startup
# Run the output command (example):
sudo env PATH=$PATH:/home/user/.nvm/versions/node/v20.11.0/bin pm2 startup systemd -u user --hp /home/user
# Save current PM2 list
pm2 save
Nginx Reverse Proxy Configuration
Nginx runs in front of your Node.js application as a reverse proxy, providing SSL termination, static file serving, and load balancing.
# Install Nginx
sudo apt update
sudo apt install nginx -y
# Create configuration file
sudo nano /etc/nginx/sites-available/myapp
server {
listen 80;
server_name example.com www.example.com;
# Static files (optional)
location /static/ {
alias /var/www/myapp/public/;
expires 30d;
add_header Cache-Control "public, immutable";
}
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_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_cache_bypass $http_upgrade;
proxy_read_timeout 86400;
}
}
# Enable the site
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
SSL Certificate (Let's Encrypt)
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Obtain SSL certificate
sudo certbot --nginx -d example.com -d www.example.com
# Test automatic renewal
sudo certbot renew --dry-run
Deployment Workflow
A simple workflow for deploying updates:
# Pull new code
cd /var/www/myapp
git pull origin main
# Update dependencies
npm install --production
# Database migration (if applicable)
npm run migrate
# Zero-downtime restart with PM2
pm2 reload myapp
# Check logs
pm2 logs myapp --lines 50
On REXE servers, you can close port 3000 via Path Panel. Since Nginx routes traffic from ports 80/443, external access to your Node.js application port is not needed.
Firewall Settings
# Open required ports with ufw
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
Conclusion
Your Node.js application is now running in a production environment managed by PM2, behind an Nginx reverse proxy, and secured with SSL. With PM2's cluster mode you can fully utilize CPU cores, and with pm2 reload you can perform zero-downtime updates.
Related Articles
Deploy PHP App: LAMP and LEMP Stack Guide
Complete guide to deploying PHP applications on a VPS. LAMP vs LEMP comparison, Apache/Nginx + PHP-FPM + MySQL setup, virtual host configuration, and PHP version management.
WordPress Installation and Optimization: VPS Guide
Guide to installing and optimizing WordPress on a VPS. LEMP stack setup, wp-config.php settings, file permissions, SSL, OPcache performance optimization, and security hardening.
Python/Django Deploy: Production with Gunicorn and Nginx
Guide to deploying Python/Django applications on a VPS. Virtualenv setup, Django production settings, Gunicorn WSGI server, Nginx reverse proxy, systemd service, and static file management.