How to run Node.js Applications with PM2?
What is PM2?
PM2 is a daemon process manager that will help you manage and keep your application online. Getting started with PM2 is straightforward, it is offered as a simple and intuitive CLI, installable via NPM.
This article will introduce you to the key features of PM2 and help you leverage it for deploying, overseeing, and scaling your Node.js applications effectively in a production environment.
How to install PM2?
The latest PM2 version is installable with NPM or Yarn:
$ npm install pm2@latest -g
# or
$ yarn global add pm2
After installation, check version.
$ pm2 --version
Start your application in production with PM2.
The simplest way to start, daemonize and monitor your application is by using this command line:
$ pm2 start server.js
Or start any other application easily:
$ pm2 start bashscript.sh
$ pm2 start python-app.py --watch
$ pm2 start binary-file -- --port 1520
Some options you can pass to the CLI:
# Specify an app name
--name <app_name>
# Watch and Restart app when files change
--watch
# Set memory threshold for app reload
--max-memory-restart <200MB>
# Specify log file
--log <log_path>
# Pass extra arguments to the script
-- arg1 arg2 arg3
# Delay between automatic restarts
--restart-delay <delay in ms>
# Prefix logs with time
--time
# Do not auto restart app
--no-autorestart
# Specify cron for forced restart
--cron <cron_pattern>
# Attach to application log
--no-daemon
As you can see many options are available to manage your application with PM2. You will discover them depending on your use case.
Check status, logs, metrics
List managed applications
With your application in motion, PM2 offers a suite of subcommands — list
, show
, and monit
—to help you monitor its performance. To get an overview of all active applications on your server, use:
$ pm2 list
You should observe the following output:
$ pm2 list
You should observe the following output:
┌─────┬─────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼─────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ --appname │ default │ 1.0.0 │ fork │ 18529 │ 3m │ 0 │ online │ 0% │ 53.3mb │ ayo │ disabled │
└─────┴─────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
This command provides a snapshot of each application’s status, uptime, memory usage, and more. If you have multiple applications running, you can sort them based on specific metrics:
$ pm2 list --sort [name|id|pid|memory|cpu|status|uptime][:asc|desc]
As in:
$ pm2 list --sort memory:desc
For a more detailed look at a specific application, use the pm2 show
command followed by the application's name or id:
$ pm2 show appname
For a live dashboard displaying metrics, metadata, and application logs, use:
$ pm2 monit
Display logs
To display logs in realtime:
$ pm2 logs
To dig in older logs:
$ pm2 logs --lines 200
Fine-tuning auto-restart strategies
PM2 employs an ecosystem.config.js
file to consolidate configuration settings for one or more applications. To generate this file in your project directory, use:
$ pm2 init simple
Upon execution, you should see:
File /home/project/appname/ecosystem.config.js generated
Edit the ecosystem.config.js
file and update the name
and script
fields to match your application:
module.exports = {
apps: [{
name: 'appname',
script: './server.js',
}]
}
With this configuration, you can manage all declared applications:
$ pm2 start ecosystem.config.js
$ pm2 restart ecosystem.config.js
$ pm2 stop ecosystem.config.js
Let’s now delve into advanced restart strategies:
Restarting based on memory usage
module.exports = {
apps: [{
name: 'appname',
script: './server.js',
max_memory_restart: '1G',
}]
}
This restarts the appname
application if memory usage surpasses 1 Gigabyte. You can also configure the max_memory_restart
option in Kilobyte (K), and Megabyte (M).
Restarting based on a CRON schedule
module.exports = {
apps: [{
name: 'appname',
script: './server.js',
cron_restart: '0 */24 * * *',
}]
}
To learn and test the cron syntax, consider using the crontab guru editor.
Delayed restarts
You can introduce a delay before PM2 restarts an application using the restart_delay
option:
module.exports = {
apps: [{
name: 'appname',
script: './server.js',
restart_delay: 5000 // wait for five seconds before restarting
}]
}
Exponential backoff restart delay
Instead of setting a fixed delay before restarting the application, you can use the exp_backoff_restart_delay
option to to raise the time between restarts up to 15 seconds incrementally. The initial delay time set through this option will be multiplied by 1.5 after each restart attempt.
module.exports = {
apps: [{
name: 'appname',
script: './server.js',
exp_backoff_restart_delay: 100 // 100ms
}]
}
With the above in place, the first restart attempt will be delayed by 100ms, second restart 150ms, then 225ms, 337.5ms, 506.25ms, and so on. This delay is reset to 0ms if the application remains online for over 30 seconds.
Setting a maximum restart limit
PM2 provides a max_restarts
option for configuring the maximum number of unstable restarts before the application is considered to have encountered an unrecoverable error. This lets you prevent your application from constantly dying and restarting, which may waste resources.
You can also specify an unstable restart through the min_uptime
option. This allows you to specify the amount of time before your application is considered "online":
module.exports = {
apps: [{
name: 'appname',
script: './server.js',
max_restarts: 16,
min_uptime: 5000, // 5 seconds
}]
}
Disabling automatic restarts
To turn off automatic restarts entirely, set the autorestart
option to false
:
module.exports = {
apps: [{
name: 'appname',
script: './server.js',
autorestart: false,
}]
}
Now, go ahead and integrate these strategies into your ecosystem.config.js
:
module.exports = {
apps: [
{
name: 'appname',
script: './server.js',
exp_backoff_restart_delay: 100,
max_memory_restart: '1G',
max_restarts: 10,
min_uptime: 2000,
},
],
};
Remove the existing appname
instance:
$ pm2 delete appname
Then, relaunch it using the configuration file:
$ pm2 start ecosystem.config.js
Sample ecosystem.config.js file:
module.exports = {
apps : [{
name: "app",
script: "./app.js",
env: {
NODE_ENV: "development",
},
env_production: {
NODE_ENV: "production",
}
}, {
name: 'worker',
script: 'worker.js'
}]
}
Launching applications on system startup
In the last section, we explored methods to keep your application resilient against unexpected crashes. In this segment, we’ll delve into how to set up a Node.js application to autostart after system reboots or crashes using PM2 and Systemd on Linux. Although PM2 is compatible with various init systems, including upstart
, launchd
, openrc
, rcd
, and systemv
, this guide focuses on Systemd.
Begin by generating an init script for your system. This script will initiate the pm2 daemon during system startup, which in turn will launch any saved applications:
$ pm2 startup systemd
To ensure that PM2 automatically starts any saved processes upon system boot, save your PM2 process list:
$ pm2 save
Managing your application logs
PM2 automatically archives logs generated by your application in the $HOME/.pm2/logs
directory. Logs directed to the standard output are stored in the <app_name>-out.log
file, while logs directed to the standard error are stored in the <app_name>-error.log
file.
ls ~/.pm2/logs
appname-error.log appname-out.log
Within the ecosystem.config.js
file, you can define the error_file
and out_file
options to specify custom locations for the application's error and output log files:
module.exports = {
apps: [
{
name: 'appname',
script: './server.js',
exp_backoff_restart_delay: 100,
max_memory_restart: '1G',
max_restarts: 10,
min_uptime: 2000,
out_file: '<custom_path>', // use /dev/null to disable
error_file: '<custom_path>', // use /dev/null to disable
},
],
};
Enabling log rotation
To ensure that your application log files are rotated before they get too large, install the pm2-logrotate module as shown below:
$ pm2 install pm2-logrotate
Afterward, when you run pm2 list
, you should see a new "Module" section like so:
┌─────┬──────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼──────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ appname │ default │ 1.0.0 │ fork │ 9408 │ 3m │ 0 │ online │ 0% │ 50.2mb │ ayo │ disabled │
└─────┴──────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
Module
┌────┬──────────────────────────────┬───────────────┬──────────┬──────────┬──────┬──────────┬──────────┬──────────┐
│ id │ module │ version │ pid │ status │ ↺ │ cpu │ mem │ user │
├────┼──────────────────────────────┼───────────────┼──────────┼──────────┼──────┼──────────┼──────────┼──────────┤
│ 1 │ pm2-logrotate │ 2.7.0 │ 12068 │ online │ 0 │ 0% │ 21.4mb │ ayo │
└────┴──────────────────────────────┴───────────────┴──────────┴──────────┴──────┴──────────┴──────────┴──────────┘
By default, the pm2-logrotate
module rotates log files once they exceed 10 megabytes. It retains up to 30 rotated files and deletes older ones. Backup files are not compressed by default. You can adjust these settings and more by referring to the module's documentation, or you use logrotate to handle log file rotation instead.
The pm2 logs
command may come in handy in development for displaying incoming application log entries in real time:
$ pm2 logs appname
To explore all available options for the logs
command and customize its output, use:
$ pm2 logs --help
Deploying your application to production
PM2 provides an integrated deployment system to facilitate deploying your application to one or multiple remote servers. To set this up, modify your ecosystem.config.js
file as follows:
module.exports = {
apps: [],
deploy: {
production: {
user: '<your_remote_server_username>',
host: [<your_remote_server_ip>],
ref: 'origin/master',
repo: '<your_git_repo_url>',
path: '/home/<your_server_username>/appname',
'post-setup': 'npm install',
'post-deploy': 'pm2 startOrRestart ecosystem.config.js --env production',
},
},
};
Here’s a breakdown of the production object properties:
user
: The username for authentication on the remote server.host
: An array of IP addresses of the remote servers.ref
: The git branch and remote to deploy, e.g.,origin/master
.repo
: The git repository's remote URL (HTTPS or SSH).path
: The directory on the remote server where the repository will be cloned.post-setup
: Commands or scripts to run post cloning.post-deploy
: Commands or scripts to run after deployment.
Before deploying to the specified host servers, ensure each has PM2 installed and the necessary permissions to clone the Git repository (e.g., the correct SSH key setup). Once set up, initiate the server provisioning with:
$ pm2 deploy production setup
If you encounter an error, it’s likely SSH-related, preventing PM2 from accessing the remote server or Git repository. Start your investigation by ensuring that the git clone <your_git_repo_url>
command works on the remote server. For troubleshooting PM2 deployments, refer to this guide.
After successful provisioning, deploy the application:
$ pm2 deploy production
$ pm2 deploy production --force # if there are local changes
Conclusion
I wrote detailed guide to server your Node.js application with PM2. But there are more features than I explained in this blog. You can see more Here.
Thanks for your reading.
Subscribe to my newsletter
Read articles from Youth Dream directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Youth Dream
Youth Dream
Accomplished Software Engineer with 6 years of experience specializing in the Python, Node.js, React and Next.js ecosystem, and a proven track record in testing and performance optimization. Demonstrated leadership in guiding teams and implementing best practices in development.