Tools: Running Your Node.js Application with PM2

Tools: Running Your Node.js Application with PM2

Prerequisites

Start the Application

Single Instance

Cluster Mode — Use All CPU Cores

With Environment Variables

With a .env File

Memory Limit — Auto Restart on OOM

Watching for File Changes (Dev/Staging Only)

Zero-Downtime Reload

Verify the Application

Save the Process List

Managing Multiple Applications

Monitoring

Real-Time Dashboard

Quick Health Check

Troubleshooting

What We Have So Far In Part 1, we installed NVM, PM2, configured log rotation, and set up startup scripts. Now let's run the actual application. That's it. PM2 is managing the process. If your app is stateless (no in-memory sessions), cluster mode spawns one process per CPU core: Or specify a fixed count: Or pass them explicitly: If your app uses dotenv, just make sure the .env file is in the app's working directory. PM2 runs from the directory where you execute the start command. Set a memory ceiling so a leaky app doesn't eat the whole server: When any instance exceeds 512MB, PM2 restarts it automatically. Don't use --watch in production. A deployment that touches multiple files triggers multiple restarts. Use pm2 reload instead. In cluster mode, reload restarts workers one at a time — no dropped requests: In fork mode (single instance), reload behaves like restart — there will be a brief downtime. After starting your app, always save: This writes the current process list to ~/.pm2/dump.pm2. On reboot, the startup script from Part 1 calls pm2 resurrect and restores everything. Skip pm2 save and your app won't come back after a reboot. Simple as that. PM2 handles multiple apps without issue: Terminal UI showing CPU, memory, logs, and metadata per process. Outputs uptime, restart count, memory usage, log file paths, and more. Common culprits: missing env vars, port conflicts, unhandled promise rejections. PM2 shows errored status: High restart_time = crash loop. Check error logs. The app is running on localhost:3000 but not exposed to the internet yet. Follow for Part 3 — Reverse Proxy with Nginx and Apache2 Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ cd /opt/myapp pm2 -weight: 500;">start server.js --name myapp cd /opt/myapp pm2 -weight: 500;">start server.js --name myapp cd /opt/myapp pm2 -weight: 500;">start server.js --name myapp ┌────┬──────┬───────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────┼───────┼───┼─────┼──────────┤ │ 0 │ myapp│ fork │ 0 │ 0% │ 45.2 MB │ └────┴──────┴───────┴───┴─────┴──────────┘ ┌────┬──────┬───────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────┼───────┼───┼─────┼──────────┤ │ 0 │ myapp│ fork │ 0 │ 0% │ 45.2 MB │ └────┴──────┴───────┴───┴─────┴──────────┘ ┌────┬──────┬───────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────┼───────┼───┼─────┼──────────┤ │ 0 │ myapp│ fork │ 0 │ 0% │ 45.2 MB │ └────┴──────┴───────┴───┴─────┴──────────┘ pm2 -weight: 500;">start server.js --name myapp -i max pm2 -weight: 500;">start server.js --name myapp -i max pm2 -weight: 500;">start server.js --name myapp -i max ┌────┬──────┬─────────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────┼─────────┼───┼─────┼──────────┤ │ 0 │ myapp│ cluster │ 0 │ 0% │ 42.1 MB │ │ 1 │ myapp│ cluster │ 0 │ 0% │ 41.8 MB │ │ 2 │ myapp│ cluster │ 0 │ 0% │ 42.3 MB │ │ 3 │ myapp│ cluster │ 0 │ 0% │ 41.5 MB │ └────┴──────┴─────────┴───┴─────┴──────────┘ ┌────┬──────┬─────────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────┼─────────┼───┼─────┼──────────┤ │ 0 │ myapp│ cluster │ 0 │ 0% │ 42.1 MB │ │ 1 │ myapp│ cluster │ 0 │ 0% │ 41.8 MB │ │ 2 │ myapp│ cluster │ 0 │ 0% │ 42.3 MB │ │ 3 │ myapp│ cluster │ 0 │ 0% │ 41.5 MB │ └────┴──────┴─────────┴───┴─────┴──────────┘ ┌────┬──────┬─────────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────┼─────────┼───┼─────┼──────────┤ │ 0 │ myapp│ cluster │ 0 │ 0% │ 42.1 MB │ │ 1 │ myapp│ cluster │ 0 │ 0% │ 41.8 MB │ │ 2 │ myapp│ cluster │ 0 │ 0% │ 42.3 MB │ │ 3 │ myapp│ cluster │ 0 │ 0% │ 41.5 MB │ └────┴──────┴─────────┴───┴─────┴──────────┘ pm2 -weight: 500;">start server.js --name myapp -i 2 pm2 -weight: 500;">start server.js --name myapp -i 2 pm2 -weight: 500;">start server.js --name myapp -i 2 PORT=3000 NODE_ENV=production pm2 -weight: 500;">start server.js --name myapp -i max PORT=3000 NODE_ENV=production pm2 -weight: 500;">start server.js --name myapp -i max PORT=3000 NODE_ENV=production pm2 -weight: 500;">start server.js --name myapp -i max pm2 -weight: 500;">start server.js --name myapp --env production \ --node-args="--max-old-space-size=512" \ -- --port 3000 pm2 -weight: 500;">start server.js --name myapp --env production \ --node-args="--max-old-space-size=512" \ -- --port 3000 pm2 -weight: 500;">start server.js --name myapp --env production \ --node-args="--max-old-space-size=512" \ -- --port 3000 cd /opt/myapp # .env file sits here alongside server.js pm2 -weight: 500;">start server.js --name myapp cd /opt/myapp # .env file sits here alongside server.js pm2 -weight: 500;">start server.js --name myapp cd /opt/myapp # .env file sits here alongside server.js pm2 -weight: 500;">start server.js --name myapp pm2 -weight: 500;">start server.js --name myapp --max-memory--weight: 500;">restart 512M pm2 -weight: 500;">start server.js --name myapp --max-memory--weight: 500;">restart 512M pm2 -weight: 500;">start server.js --name myapp --max-memory--weight: 500;">restart 512M pm2 -weight: 500;">start server.js --name myapp --watch --ignore-watch="node_modules logs" pm2 -weight: 500;">start server.js --name myapp --watch --ignore-watch="node_modules logs" pm2 -weight: 500;">start server.js --name myapp --watch --ignore-watch="node_modules logs" pm2 reload myapp pm2 reload myapp pm2 reload myapp # PM2 -weight: 500;">status pm2 -weight: 500;">status # Port listening? ss -tlnp | grep 3000 # Hit the app -weight: 500;">curl http://localhost:3000 # Logs pm2 logs myapp --lines 50 # PM2 -weight: 500;">status pm2 -weight: 500;">status # Port listening? ss -tlnp | grep 3000 # Hit the app -weight: 500;">curl http://localhost:3000 # Logs pm2 logs myapp --lines 50 # PM2 -weight: 500;">status pm2 -weight: 500;">status # Port listening? ss -tlnp | grep 3000 # Hit the app -weight: 500;">curl http://localhost:3000 # Logs pm2 logs myapp --lines 50 pm2 -weight: 500;">start /opt/app1/server.js --name api -i 2 pm2 -weight: 500;">start /opt/app2/app.js --name frontend pm2 -weight: 500;">start /opt/app3/worker.js --name worker pm2 save pm2 -weight: 500;">start /opt/app1/server.js --name api -i 2 pm2 -weight: 500;">start /opt/app2/app.js --name frontend pm2 -weight: 500;">start /opt/app3/worker.js --name worker pm2 save pm2 -weight: 500;">start /opt/app1/server.js --name api -i 2 pm2 -weight: 500;">start /opt/app2/app.js --name frontend pm2 -weight: 500;">start /opt/app3/worker.js --name worker pm2 save ┌────┬──────────┬─────────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────────┼─────────┼───┼─────┼──────────┤ │ 0 │ api │ cluster │ 0 │ 0% │ 42.1 MB │ │ 1 │ api │ cluster │ 0 │ 0% │ 41.8 MB │ │ 2 │ frontend │ fork │ 0 │ 0% │ 38.5 MB │ │ 3 │ worker │ fork │ 0 │ 0% │ 35.2 MB │ └────┴──────────┴─────────┴───┴─────┴──────────┘ ┌────┬──────────┬─────────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────────┼─────────┼───┼─────┼──────────┤ │ 0 │ api │ cluster │ 0 │ 0% │ 42.1 MB │ │ 1 │ api │ cluster │ 0 │ 0% │ 41.8 MB │ │ 2 │ frontend │ fork │ 0 │ 0% │ 38.5 MB │ │ 3 │ worker │ fork │ 0 │ 0% │ 35.2 MB │ └────┴──────────┴─────────┴───┴─────┴──────────┘ ┌────┬──────────┬─────────┬───┬─────┬──────────┐ │ id │ name │ mode │ ↺ │ cpu │ memory │ ├────┼──────────┼─────────┼───┼─────┼──────────┤ │ 0 │ api │ cluster │ 0 │ 0% │ 42.1 MB │ │ 1 │ api │ cluster │ 0 │ 0% │ 41.8 MB │ │ 2 │ frontend │ fork │ 0 │ 0% │ 38.5 MB │ │ 3 │ worker │ fork │ 0 │ 0% │ 35.2 MB │ └────┴──────────┴─────────┴───┴─────┴──────────┘ pm2 show myapp pm2 show myapp pm2 show myapp pm2 logs myapp --err --lines 100 pm2 logs myapp --err --lines 100 pm2 logs myapp --err --lines 100 -weight: 600;">sudo lsof -i :3000 -weight: 600;">sudo lsof -i :3000 -weight: 600;">sudo lsof -i :3000 pm2 describe myapp pm2 describe myapp pm2 describe myapp - NVM, Node.js, and PM2 installed (Part 1) - A Node.js application with a server.js or app.js entry point - Dependencies installed (-weight: 500;">npm -weight: 500;">install or -weight: 500;">npm ci) - ✅ Part 1: NVM + PM2 + startup + log rotation - ✅ Part 2: App running with PM2, cluster mode, memory limits, zero-downtime reloads