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-app
for 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.