FTP uploads and ad‑hoc scripts make apps fragile. A tidy PM2 + Nginx setup gives you predictable behavior, structured logs, and safe updates while keeping resource usage efficient.
Table of Contents
- Prerequisites
- Install Node.js (LTS) and PM2
- Create
ecosystem.config.js - Configure Nginx reverse proxy
- Add HTTPS with Let's Encrypt
- Logs & logrotate
- Graceful reloads & rolling restarts
- Scale with PM2 cluster mode
- FAQs
1) Prerequisites
- SSH access to your hosting account or server
- Node.js LTS (v18+ recommended) and Git installed
- Domain pointed to the server (A/AAAA records)
2) Install Node.js (LTS) and PM2
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - sudo apt-get install -y nodejs build-essential sudo npm i -g pm2 pm2 startup # prints a system command — run it to enable auto‑start
3) Create ecosystem.config.js
module.exports = {
apps: [{
name: "my-app",
script: "server.js",
instances: "max",
exec_mode: "cluster",
env: { NODE_ENV: "production", PORT: 3000 },
max_memory_restart: "512M",
out_file: "~/logs/my-app.out.log",
error_file: "~/logs/my-app.err.log",
merge_logs: true,
watch: false
}]
}
# start & save
pm2 start ecosystem.config.js && pm2 save
4) Configure Nginx reverse proxy
# /etc/nginx/sites-available/my-app.conf
server {
listen 80;
server_name example.com www.example.com;
location / {
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_pass http://127.0.0.1:3000;
proxy_read_timeout 60s;
}
# static assets (optional micro cache)
location ~* \.(css|js|svg|ico|png|jpg|jpeg|webp)$ {
expires 7d;
add_header Cache-Control "public";
try_files $uri @app;
}
location @app { proxy_pass http://127.0.0.1:3000; }
}
# enable and test
sudo ln -s /etc/nginx/sites-available/my-app.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
5) Add HTTPS with Let's Encrypt
sudo apt-get install -y certbot python3-certbot-nginx sudo certbot --nginx -d example.com -d www.example.com # auto‑renew sudo systemctl enable certbot.timer && sudo systemctl start certbot.timer
6) Logs & logrotate
- Use
pm2 logs my-appfor live tails - Rotate with
logrotate(daily or size‑based) to avoid disk bloat - Ship structured logs to an external store if needed
7) Graceful reloads & rolling restarts
# apply code changes without dropping connections pm2 reload my-app # restart one instance at a time for stability pm2 restart my-app --update-env
8) Scale with PM2 cluster mode
- Use
instances: "max"to span CPU cores - For WebSocket/session apps, consider sticky sessions at the proxy
- Watch CPU/RAM and right‑size your plan before adding more features
Launch a robust Node.js stack
Run PM2 in cluster mode behind Nginx with automatic HTTPS and clean logs.
9) FAQs
Do I need root access?
Full root is convenient, but many hosts support PM2 and Nginx on managed plans. Check what your plan allows.
Can I use Apache instead of Nginx?
Yes. Use ProxyPass/ProxyPassReverse with similar headers. Nginx is popular for its lean footprint.
How do I set environment variables?
Define them in ecosystem.config.js (env: {}) or your shell profile; avoid committing secrets to Git.