How to Configure PM2 the right way
Tags: node, javascript, tutorial, beginners
Published: 2022-11-08 08:25:54

### Prelude
While developing apps on NodeJS, you must have used packages like Nodemon, which monitors your code for changes and restarts your Node App. Though Nodemon is super useful in development, it's not recommended to use it in Production. Nodemon is not something you use if you wish to keep your app alive on Production if it crashes.

Here's where PM2 comes in handy. I've been hosting my NodeJS apps using PM2 on production as well I use PM2 for Development.

<u>Quoting from PM2</u>:
>PM2 is a production process manager for Node.js applications with a built-in load balancer. It allows you to keep applications alive forever, to reload them without downtime, and to facilitate common system admin tasks.

Before we go ahead and configure the PM2, yes, PM2 does support the auto-reload feature like Nodemon using the `watch` feature. More on this later in the article.

---

### Configuring PM2
PM2 simplifies the way you could define the configuration for your app using PM2 ecosystem configuration file. You could use a PM2 configuration file for configuring multiple envs, logging, etc.

Here's a snippet for configuring your PM2 envs and I'll brief each of these later in the article.

#### ecosystem.config.js

```javascript
module.exports = {
  apps: [
    {
      name: "PROJECT_NAME",
      script: "./server.js",
      instances: 2,
      max_memory_restart: "300M",
      
      // Logging
      out_file: "./out.log",
      error_file: "./error.log",
      merge_logs: true,
      log_date_format: "DD-MM HH:mm:ss Z",
      log_type: "json",

      // Env Specific Config
      env_production: {
        NODE_ENV: "production",
        PORT: 8080,
        exec_mode: "cluster_mode",
      },
      env_development: {
        NODE_ENV: "development",
        PORT: 8080,
        watch: true,
        watch_delay: 3000,
        ignore_watch: [
          "./node_modules",
          "./app/views",
          "./public",
          "./.DS_Store",
          "./package.json",
          "./yarn.lock",
          "./samples",
          "./src"
        ],
      },
    },
  ],
};
```
> You can put the configuration file at the root level of your project.

---

### Basic Configuration
1. #### name
This could be your project name and PM2 will use this name to name your workers.

2. #### script
The entry point of your app, mostly it's the `server.js`. Make sure you set the right path, the above configuration assumes that you've the `ecosystem.config.js` and the `server.js` file at the root level of your project.

3. #### instances
This is the number of workers you wish to spawn. 

4. #### max_memory_restart
PM2 allows you to reload your application based on the memory limit you specify using the `max_memory_restart`.

---

### Logging Configuration

1. #### out_file
Generic logging, all your `console.log()` statements are logging in this file along with other information.

2. #### error_file
Similar to `out_file` but error files only log the errors of your application.

3. #### merge_logs
This is not needed if you are fine with PM2 storing a unique log file for each of its PID - Process ID). Using this config will merge all your app logs into a single file. <u>`merge_logs` will merge your log files of various PIDs and it **doesn't merge** the output and error logs into one.</u>

4. #### log_date_format
The above is self-explanatory. You could define a date/time format you wish to print your logs in.

5. #### log_type
Specify the output style, it's raw by default but we are using JSON here.

  > <u>**Logs Path & Disabling Logs**</u>
  By default, your Logs are stored under `$HOME/.pm2/logs/<app name>-(out||error)-<pid>.log`. You could disable logging by pointing the log path to `out_file: "/dev/null"` and the same is applicable for `error_file`

---

### Environment specific Configuration

<ol>
<li>
<h4>env_production && env_development</h4>
These are nothing but different environments you wish to run your apps in, using PM2.

You could create config for multiple staging env if your setup requires to do so.
<ul>
<li>
<h5><u>NODE_ENV</u></h5>
The above set the env variable value to Production / Development for your app.
</li>
<li>
<h5><u>PORT</u></h5>
Port at which you would like to initiate your app at.
</li>
</ul>
</li>
<li>
<h4>exec_mode</h4>
You could use cluster or fork, where the latter is the default mode. It is recommended to use the cluster mode on production and you could use a single instance locally. Number specified in the instances creates child processes (workers) and distributes the incoming connections across the worker processes.

You could set the value to 0 or max to specify how many workers you wish the PM2 should launch (max will spawn as many workers as the number of available cores).

<b>From <a href="https://nodejs.org/api/cluster.html#cluster_how_it_works">PM2 Docs</a></b>
<blockquote>
The worker processes are spawned using the child_process.fork() method, so that they can communicate with the parent via IPC and pass server handles back and forth.
</blockquote>
</li>
<li>
<h4>watch</h4>
We need to use this only on the dev envs. The use case is exactly what nodemon does. The watch monitors the file changes in your app and restarts your app as soon as a file change is detected.
</li>
<li>
<h4>watch_delay</h4>
You don't want to end up restarting your app as soon as you save your file. So if you try to save your file multiple times in under three seconds, it will only restart your app once as it will clear the watch timer every time you save your files under three seconds.
</li>
<li>
<h4>ignore_watch</h4>
There will your app will end up restarting even if you install an NPM package, add or move a file in your app dir, or as simple as add an img in your public dir.

Hence, any folder or file paths added to the ignore_watch array will not trigger a restart of your app as they are not relevant and don't require your app to be restarted in the first place.
</li>
</ol>

---

### Useful PM2 commands

#### Starting your App in Dev & Production mode
You could add the following commands to `package.json`
```json
"scripts": {
  "start_dev": "pm2 start ecosystem.config.js --env development",
  "start_prod": "pm2 start ecosystem.config.js --env production"
},
```

You could execute the above commands like `npm run start_dev` and `npm run start_prod` which starts your app in dev and production mode with the given config.

---

#### Start, Stop, Restart & Kill your App

##### Kill your App
```bash
pm2 kill #kills all pm2 workers
```

##### Reloading your App
```bash
pm2 reload all
```

##### Restarting your App
```bash
pm2 restart all
```

Alternatively, you could pass the PID instead of using `all` keyword which will end up running the same operation on all the processes.

> Restarting a process will kill and restart it as opposed to reloading which achieves a 0-second-downtime reload.


---

#### Logs
##### Delete all Logs
```bash
pm2 flush
```

##### Read Logs
```bash
pm2 logs
```

---

#### Processes
##### List Processes
```bash
pm2 list
```

##### Monitor Processes
```bash
pm2 monit
```

---

### References
You could also configure the deployment process using the PM2 but I've not covered it in the above tutorial as I've never used it personally.

Here are some resources which will help you understand PM2 configuration in detail.

- [PM2](https://www.npmjs.com/package/pm2)
- [Configuration Documentation](https://pm2.keymetrics.io/docs/usage/application-declaration/)
- [Cluster vs Fork in PM2](https://stackoverflow.com/questions/34682035/cluster-and-fork-mode-difference-in-pm2)