Advanced Node.js Process Management with PM2

Matthew Brown JavaScript, Node.js, Technology Snapshot, Tutorial 1 Comment

If you’ve worked on any front end applications recently, you have likely had some contact with a Node/Express application. What you may not be familiar with is how these applications are managed in production.

I recently had a project where I needed to set up a Node application on an AWS server. I needed a tool to solve the technical challenges of managing the Node processes and deploying the application in a very efficient manner.

I chose PM2, which is a handy process management tool for running Node.js applications in production environments. PM2 provides a simple command line interface that makes it easy to get started. This powerful Node module has tools for managing application processes, logging, and more.

In this article, I provide an introduction to PM2, showing why it is such a valuable tool for managing Node.js applications. By way of a reference example application, we show the basic features and commands for using PM2 and give examples of generating configuration files for both running and deploying applications.

PM2

Example Node.js Application

To get started, we need to create a very simple example application. Use the following commands to create a new folder for the example app:

$ mkdir pm2-example-app
$ cd pm2-example-app
$ npm init -y

This will generate a basic package.json file which we will want to update the main line to use app.js rather than index.js:

{
  "name": "pm2-example-app",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Next, install Express:

$ npm install express --save

And now that we are all set up we can add the code in a new file: app.js.

var express = require('express');
var app = express();

app.get('/', (req, res) => {
  console.log('GET /');
  res.send('Hello World!');
});

app.listen(3000, () => {
  console.log('Listening on port 3000');
});

And that is it for our example app. Now we can start showing how PM2 can be used.

PM2’s Command Line Interface

We will start with a few of the basic commands from the CLI. To use PM2, you will need to install it globally through npm:

$ npm install -g pm2@latest

And then we will use pm2 start to start the application.

$ pm2 start app.js

PM2 will print out some information in your terminal about the process you just started. You can then use the process ID to manage the process. So since 2065 is the pid of the process I just started, I can manage it with the following commands:

$ pm2 stop 2065
$ pm2 restart 2065
$ pm2 reload 2065

Most of these commands should be pretty self-explanatory. The only difference between reload and restart is the reload will restart the process(es) with zero downtime.

Alternatively, you can specify all rather than pid to affect all current processes. For example, this command will remove all existing processes.

$ pm2 delete all

Cluster Mode

One of the most powerful features of PM2 is the ability to run in cluster mode. This will run Node processes across all available CPUs. This greatly increases your application’s ability to handle traffic without having to change any code.

Running multiple Node processes across all the available CPUs acts like a load balancer. So, as long as you are using a server with more than one CPU available, then you are increasing the reliability.

Use the following command to start our example app in cluster mode:

$ pm2 start app.js -i max

Note that I have used max here as a parameter, which means PM2 will auto-detect the number of CPUs available. You can also pass a number as well if you want to limit the number of CPUs used.

You can see from my screen shot that I now have the example application running in 8 different processes. Let’s monitor these processes with this command:

$ pm2 monit

This will bring up a dashboard listing all the processes currently running though PM2 along with some other metrics like current CPU and memory usage.

We can use the following command to simulate some traffic to our application:

curl -s "http://localhost:3000?[1-1000]"

This should cause our processes to log the GET requests in the Global logs on the monitoring dashboard.

You can also stream the logs with the following command:

$ pm2 logs

Generating a Configuration File

Up until now we have been using the CLI to manually tell PM2 how to manage our processes. Ideally when deploying this, we want to have a configuration file that will take care of automating this for us.

Generate the default configuration file using the following command:

$ pm2 ecosystem simple

PM2 generates aecosystem.config.js file. Here is what it looks like:

module.exports = {
  apps : [{
    name   : "app1",
    script : "./app.js"
  }]
}

Now this is a little basic, so let’s fill this out a bit more.

module.exports = {
  apps : [{
    name        : "pm2-example-app",
    script      : "./app.js",
    watch       : true,
    instances   : 4,
    exec_mode   : "cluster",
    env: {
      "PORT": 3000,
      "NODE_ENV": "development"
    },
    env_staging : {
      "PORT": 80,
      "NODE_ENV": "staging"
    },
    env_production : {
      "PORT": 80,
      "NODE_ENV": "production"
    }
  }]
}

Now let’s pause and break down what we just did here.

You will notice the property apps is an array of JavaScript objects. So, if you’re in a situation where you have many Node applications that will be running on the same server, then they all can be controlled from the same ecosystem configuration.

The script is the same app script that we had used previously. Setting the watch property to true tells PM2 to watch for code changes and automatically reloads if there are any. Then the next two properties, instances and exec_mode, are for specifying the number of CPUs and cluster mode as we had discussed previously.

Then the other properties are for declaring different environment variables. By default it will run with the env environment, and you can see we have designated that as our development environment. We have also added a staging environment and production environments.

To execute the application with these, we need to pass in a env parameter on start or restart.

$ pm2 start ecosystem.config.js --env staging
$ pm2 start ecosystem.config.js --env production

Deployments

PM2 can also be configured to handle deployments. Take a look at an example setup for a production deployment below. This would belong in the ecosystem config below the apps property.

"deploy" : {
  "production" : {
    "key"  : "/path/to/key.pem",  // path to the private key to authenticate
    "user" : "node",              // user used to authenticate
    "host" : "111.11.111.1",      // where to connect
    "ref"  : "origin/master",
    "repo" : "git@github.com:repo.git",
    "path" : "/var/www/production",
    "post-deploy" : "pm2 startOrRestart ecosystem.config.js --env production"
  },
}

So in order to set this up, you will need to tell PM2 the path to your .pem key file, the host server’s IP address, and user name used for connecting to the host. ref refers to the Git branch required (usually master for production) and repo which is the path to the source code repository. path is the path on the host server where you want to deploy the source code.

Then lastly post-deploy allows you to specify a command to run after the deployment is complete. What we have specified here will start of restart the processes to run within the production environment.

To execute deployments we would use the following command locally. This will handle cloning the latest Git repository code on the server host and then using PM2 to start or restart the Node processes.

$ pm2 deploy ecosystem.config.js production

Recap

We have covered quite a bit of ground here, so let’s recap. We discussed what PM2 is & why it is such a valuable tool for managing Node.js applications. Then we covered the basic features and commands for using PM2 and also gave examples of generating configuration files for both running and deploying applications.

Thank you for following along and if you have a need to manage and deploy Node.js applications I hope you have learned a bit about using PM2 to make that process easier. To learn more about what else PM2 can do be sure to check out the project page here.


About the Author
Matthew Brown

Matthew Brown

I am a web application developer with experience in a wide variety of fields such as agriculture, finance and freight. I am a JavaScript enthusiast and enjoy learning all new technologies. When I’m not coding I am enjoying the Kansas City lifestyle with my wife and three dogs and currently learning how to play the guitar!


Share this Post

Comments 1

Leave a Reply