Chronicles of the Afro Kid
Chronicles of the Afro Kid

Managing and Deploying NodeJS Apps with PM2

Riyadh Al Nur

I use PM2 as the process manager for my NodeJS apps in production and staging servers among others.

PM2, now has a deployment pipeline built-in but the documentation is a bit under done in my opinion. So after, digging around in the code and documentation, I pieced together this guide to get you started on using PM2 not just to manage your NodeJS apps, but deploy them to servers as well.

TL;DR I use PM2 to deploy for mid to small sized apps which have a fixed number of servers running the applications. For applications where I need to spin up servers automatically using the same configurations, when needed, I rely on Ansible to spin up new servers and deploy to them.

To get started, install pm2 as a global package on your local machine -
npm install -g pm2

Once that is done, navigate to your project folder and create the deployment file using pm2 ecosystem. This generates a ecosystem.json file in your project folder which contains sample configurations for managing PM2 applications and deployment pipeline. Here, you can opt to just generate a file under a different name, e.g deploy.json. You necessarily don't have to use ecosystem.json as the file.

Open the newly created/generated .json file in your editor. This is what a typical description file will look like:

{
  "apps" : [
    {
      "name": "myApp",
      "script": "server.js",
      "instances": 2,
      "exec_mode": "cluster_mode",
      "env_production": {
        "NODE_ENV": "production",
        "MONGO_URI": "mongodb://myuser:mypassword@192.168.0.000:27017/myApp?authSource=admin",
        "COOKIE_SECRET": "mySecret"
      }
    }
  ],
  "deploy": {
    "production": {
      "key": "myApp.pem",
      "user": "node",
      "host": ["<ip-server>", "<ip-server>"],
      "ref": "origin/master",
      "repo": "git@github.com:myApp/myApp.git",
      "path": "/var/www/myApp",
      "post-deploy" : "mkdir -p logs && touch logs/all-logs.log && npm install --production && pm2 startOrRestart ecosystem.json --env production",
      "pre-deploy-local" : "echo 'Deploying code to servers'"
    }
  }
}

In lines 2-14, the apps array contains the definition for your application that PM2 needs to manage.

Lines 6-7 is required if you want your application to make use of multi-core servers. PM2 uses Node's internal cluster module to make this possible under the hood.

Lines 8-11 contain the environment variables that your application requires.

Moving on, line 15 and onwards is the start of the configuration for deploying your application to your servers. Under the deploy, we define a separate object for each environment we want for deploys. Under each environment configuration, we have the following keys -

  1. key - This is the key file required to authenticate with your server. PM2 uses SSH to connect to your servers. If you don't specify a key file, you would need to add your local machine's SSH keys to the list of authorized_keys on your servers. If not already obvious already, doing this is tedious if you have more than 1 server and you have a team working on the application.
  2. user - This is the user that PM2 will login as.
  3. host - The host can either be a string or an array of strings containing the IP address of your servers.
  4. ref - This is the branch you want to deploy to your servers - for production, it's usually master. You can then have origin/staging for your staging servers etc.
  5. repo - Stating the obvious here but this is the address for your repo hosted on GitHub, GitLab or Bitbucket and so on.
  6. path - This is the path on your servers that you want to run your application from.
  7. post-deploy - These are shell commands you want to run after code has been deployed. The last 2 parts are recommended (if not required because otherwise you would have to manually restart your apps for changes to take effect) because it installs your application dependencies and then restarts the application gracefully (if already running) or starts it. The first 2 parts are what I use to create the logs folder and create the log file inside the folder.
  8. pre-deploy-local - This is required when you want to run shell commands on your local machine before code is deployed to your servers but for me, I only display a message on my terminal.

After you have configured the file, save it and exit the editor. In your terminal from the project root folder (or where ever you have placed the .json file in your project folder), run pm2 deploy ecosystem.json production setup. The setup part at the end is for first time deploys to newly spun up servers. This command will connect to your servers and set up the folders and clone your repo on to your machines.

If you get an error message during the setup process that the connection failed, it is usually due to your server not being able to authenticate the RSA fingerprint of your git servers. To get around it, you can log on to your servers beforehand and clone the repo to a random folder so that the fingerprint gets added to your list of known_hosts or you can add it yourself to the list if you already know the fingerprints for the git servers.

Lastly, after the set up is done, run pm2 deploy ecosystem.json production to deploy code to your servers. PM2 will automatically start/restart the applications as defined in your configuration file and if everything goes well, will exit with a SUCCESS message at the end.

Navigate to your application's URL in the browser and you should see your app running on your newly created servers.