In the past, on my server I’ve installed Fossil, Syncthing, Bitwarden, Inlets. All these require some form of Process Management. These programs / processes / services need to be always running, so they are restarted in case they get terminated. Also, on a system restart these need to start up automatically.

I’ve mostly used Systemd or Supervisor to all these things.

However, I recently came across PM2 and I was really blown out of my mind as to how simple it was to set it up.

It’s a NodeJS application, so that needs to be there.

Installation

Installing it globally means to run –

npm install -g pm2

Of course it works seamlessly with Node applications

Starting applications

So to start a Node application in the background, all we need to do is –

pm2 start app.js

But to run a ruby application is as simple! So to run a rails application –

pm2 start rails

For that matter, to run a shell script would be –

pm2 start bashscript.sh

By default, PM2 process takes the name of the file that is run. To explicitly assign a name for a rake task would be –

pm2 start "bundle exec rake app:notifications" --name "app notifications"

Additionally, there are a whole lot of other options that can be passed in.

Once the processes are running, entering pm2 status gives us a very nice output.

https://i.imgur.com/LmRD3FN.png
pm2 status

Managing processes

To manage the processes is also extremely simple.

pm2 restart app_name
pm2 reload app_name
pm2 stop app_name
pm2 delete app_name

And even to get a realtime dashboard of all processes running, the command to run is pm2 monit

https://i.imgur.com/xo0LDb7.png
pm2 monit

Managing multiple applications

To manage many applications from one place, we can create an ecosystem file by running –

pm2 ecosystem

This creates an ecosystem.config.js file which looks like this –

module.exports = {
  apps : [{
    name: "app",
    script: "./app.js",
    env: {
      NODE_ENV: "development",
    },
  }, {
     name: 'worker',
     script: 'worker.js'
  }]
}

Since I wanted a single place to manage everything from, that’s what I did.

To start pm2 with this ecosystem.config.js

pm2 start ~/pm2/ecosystem.config.js

Reading environment variables from outside the ecosystem file

I had another challenge though – for one of the applications I needed to have an environment variable set and I didn’t want that in the ecosystem.config.js file. I wanted it in a separate file from which it could be read.

Since the ecosystem.config.js is a simple javascript file, we can add some code for that –

const fs = require('fs');
const path = require('path');
const pathToTokenFile = path.join('/path/to/token_file');
const appToken = () => {
  const token = fs.readFileSync(pathToTokenFile, 'utf8');;
  return token;
}

module.exports = {
  apps : [{
    name: "app",
    script: "./app.js",
    env: {
      NODE_ENV: "development",
      AUTHTOKEN: appToken()
    },
  }, {
     name: 'worker',
     script: 'worker.js'
  }]
}

Start applications on boot

To create a script which will start all my applications at boot time –

pm2 startup

This will give an output which which is basically a command that I need to execute manually

sudo env PATH=$PATH:/path/to/node /path/to/pm2 startup systemd -u myusername --hp /my/home/directory

Essentially this command will create a systemd service which will start PM2 automatically and then PM2 will start all the applications.

And just in case we want to remove it from startup.

pm2 unstartup systemd

PM2 is truly an amazing piece of software!

Rarely have I seen a software which does so many complex things in such a simple manner. Kudos to them!