VOOZH about

URL: https://dev.to/chasebot/pm2-process-manager-keep-your-nodejs-bots-running-forever-5g01

⇱ pm2 Process Manager: Keep Your Node.js Bots Running Forever - DEV Community


What You'll Need

  • n8n Cloud or self-hosted n8n (for orchestrating automated workflows)
  • Hetzner VPS or Contabo VPS for hosting your Node.js processes
  • DigitalOcean as an alternative hosting provider
  • Node.js 18+ installed locally and on your server
  • SSH access to your VPS
  • PM2 (we'll install this together)
  • A text editor (VS Code recommended)

Table of Contents

  1. Why PM2 Matters for Bot Automation
  2. Installation & Initial Setup
  3. Running Your First Process
  4. Clustering & Load Balancing
  5. Monitoring & Logs in Real-Time
  6. Auto-Restart on Server Reboot
  7. Integration with n8n Webhooks
  8. Getting Started

Why PM2 Matters for Bot Automation

I learned this the hard way. Three years ago, I deployed a Node.js bot to a VPS—no process manager. It crashed at 2 AM on a Sunday, sat dead for six hours, and tanked the entire automation pipeline. That was the day I discovered PM2.

If you're running Node.js applications in production—whether it's a webhook listener for n8n automation, a Telegram bot, a Discord integration, or a custom API server—you need process management. PM2 is the industry standard because it:

  • Restarts crashed processes automatically (no manual intervention)
  • Clusters your app across CPU cores (load balancing built-in)
  • Manages logs (no more digging through syslog)
  • Survives server reboots (auto-start on startup)
  • Monitors memory and CPU (alerts when things go wrong)
  • Handles zero-downtime deployments (reload without dropping connections)

When you're building automation workflows that depend on external services, uptime isn't optional. Unlike managed platforms, a VPS doesn't come with built-in process monitoring. PM2 fills that gap for under $5/month in hosting costs (plus zero subscription fees).


Installation & Initial Setup

SSH into your server and install PM2 globally:

sudo npm install -g pm2

Verify the installation:

pm2 --version

You should see version 5.x or higher. Next, create a directory for your bot project:

mkdir -p ~/projects/automation-bot
cd ~/projects/automation-bot
npm init -y

Install Express and Axios as dependencies (we'll use these for webhook handling):

npm install express axios dotenv

Now create your first .env file to store configuration:

cat > .env << 'EOF'
PORT=3001
NODE_ENV=production
N8N_WEBHOOK_URL=https://your-n8n-instance.com/webhook/automation
LOG_LEVEL=info
EOF

Running Your First Process

Let me show you a real example. This is a simple Express server that listens for webhooks and triggers actions. Create app.js:

const express = require('express');
const axios = require('axios');
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 3001;

app.use(express.json());

// Health check endpoint
app.get('/health', (req, res) => {
 res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});

// Webhook endpoint
app.post('/webhook/event', async (req, res) => {
 try {
 const { event_type, data } = req.body;

 console.log(`[${new Date().toISOString()}] Received event: ${event_type}`);

 // Send to n8n webhook
 const response = await axios.post(process.env.N8N_WEBHOOK_URL, {
 event_type,
 data,
 received_at: new Date().toISOString(),
 server_hostname: require('os').hostname()
 });

 res.status(200).json({ 
 success: true, 
 message: 'Event processed',
 n8n_response: response.status 
 });
 } catch (error) {
 console.error(`[ERROR] ${error.message}`);
 res.status(500).json({ 
 success: false, 
 error: error.message 
 });
 }
});

// Error handling middleware
app.use((err, req, res, next) => {
 console.error('Unhandled error:', err);
 res.status(500).json({ error: 'Internal server error' });
});

app.listen(PORT, () => {
 console.log(`[${new Date().toISOString()}] Server running on port ${PORT}`);
 console.log(`Health check: http://localhost:${PORT}/health`);
});

// Graceful shutdown
process.on('SIGTERM', () => {
 console.log('SIGTERM received, shutting down gracefully');
 process.exit(0);
});

Now start this with PM2:

pm2 start app.js --name "webhook-bot"

Check the status:

pm2 status

You'll see output like:

┌─────┬──────────────┬─────────────┬──────┬────────┬──────────┐
│ id │ name │ namespace │ mode │ status │ restart │
├─────┼──────────────┼─────────────┼──────┼────────┼──────────┤
│ 0 │ webhook-bot │ default │ fork │ online │ 0 │
└─────┴──────────────┴─────────────┴──────┴────────┴──────────┘

Test it:

curl -X POST http://localhost:3001/webhook/event \
 -H "Content-Type: application/json" \
 -d '{"event_type":"test","data":{"message":"hello"}}'

💡 Fast-Track Your Project: Don't want to configure this yourself? I build custom n8n pipelines and bots. Message me with code SYS3-DEVTO.


Clustering & Load Balancing

Here's where PM2 gets powerful. Instead of running a single process, you can spawn one worker per CPU core. This means if your server has 4 cores, PM2 will automatically load-balance across 4 instances of your app.

Create an ecosystem.config.js file in your project root:

module.exports = {
 apps: [
 {
 name: 'webhook-bot',
 script: './app.js',
 instances: 'max',
 exec_mode: 'cluster',
 env: {
 NODE_ENV: 'production',
 PORT: 3001
 },
 merge_logs: true,
 autorestart: true,
 watch: false,
 max_memory_restart: '500M',
 error_file: './logs/err.log',
 out_file: './logs/out.log',
 log_date_format: 'YYYY-MM-DD HH:mm:ss Z'
 }
 ]
};

Stop the current process and restart using this config:

pm2 stop webhook-bot
pm2 start ecosystem.config.js

Check the cluster:

pm2 status

Now you'll see multiple instances (one per core):

┌─────┬──────────────┬─────────────┬──────────┬────────┬──────────┐
│ id │ name │ namespace │ mode │ status │ restart │
├─────┼──────────────┼─────────────┼──────────┼────────┼──────────┤
│ 0 │ webhook-bot │ default │ cluster │ online │ 0 │
│ 1 │ webhook-bot │ default │ cluster │ online │ 0 │
│ 2 │ webhook-bot │ default │ cluster │ online │ 0 │
│ 3 │ webhook-bot │ default │ cluster │ online │ 0 │
└─────┴──────────────┴─────────────┴──────────┴──────────┴──────────┘

This is key for production. If one instance crashes, PM2 auto-restarts it while the others keep handling requests. When you're comparing automation platforms—whether it's n8n vs Make vs Zapier—self-hosted solutions like n8n running on your own VPS require this kind of reliability layer.


Monitoring & Logs in Real-Time

Watch all your processes in a dashboard:

pm2 monit

This shows CPU, memory, requests per minute, and restart counts. Press q to exit.

View logs for a specific app:

pm2 logs webhook-bot

Or tail the last 100 lines:

pm2 logs webhook-bot --lines 100

For a broader view of what's happening, save all logs to files. The ecosystem.config.js I provided earlier already does this. Check your logs:

tail -f ~/projects/automation-bot/logs/out.log

You can also rotate logs automatically. Install the PM2 logrotate module:

pm2 install pm2-logrotate

Configure it to keep logs under 10MB:

pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7

This keeps your disk clean—critical when you're running high-volume webhook receivers on a budget VPS like Hetzner or Contabo.


Auto-Restart on Server Reboot

If your VPS restarts (whether planned or due to a crash), PM2 needs to resurrect your processes. We do this by creating a startup script:

pm2 startup

This command outputs a line you need to run. It looks like:

sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu

Copy and run that exact line. Then save your current process list:

pm2 save

Now test it. Reboot your server:

sudo reboot

Wait 30 seconds, SSH back in, and check:

pm2 status

Your processes should be running. If they're not, check the systemd service:

systemctl status pm2-ubuntu

Integration with n8n Webhooks

This is where it gets practical. When you're running a self-hosted n8n instance, you often need a separate Node.js service to handle incoming webhooks and trigger workflows. PM2 keeps that service alive.

Here's a real workflow: your PM2-managed bot listens on port 3001, receives events, and hits your n8n webhook to trigger an automation.

Modify your .env:

cat > .env << 'EOF'
PORT=3001
NODE_ENV=production
N8N_WEBHOOK_URL=https://your-n8n-domain.com/webhook/

Originally published on Automation Insider.