Table of contents
- Step 1: Installing PM2
- Step 2: Running Your First Node.js File with PM2
- Step 3: Running npm Scripts with PM2
- Step 4: Managing Multiple npm Scripts
- Step 5: Basic PM2 Commands
- Step 6: Monitoring Your Application
- Step 7: Reloading Without Downtime
- Step 8: Managing Logs
- Step 9: Automatic Restart on Crashes
- Real-world Example: MERN Stack
- Conclusion
Welcome to your journey with PM2, the powerful process manager for Node.js applications. Whether you're a beginner just starting with Node.js or an experienced developer looking to optimize your application management, this guide will walk you through PM2 from the ground up. We'll focus primarily on managing a MERN (MongoDB, Express, React, Node.js) stack application, but the principles apply to any Node.js project.
Ok, before discussing other things. Let’s answer the main question that you may have in your mind and that is - What is PM2?
PM2 is a production-grade process manager for Node.js applications. It is designed to help manage, monitor, and keep applications running continuously. PM2 provides features such as automatic application restarts on failure, and comprehensive log management. This tool simplifies the deployment and management of Node.js applications by enabling efficient resource use and ensuring high availability, making it especially useful for managing web servers, APIs, and complex Node.js services in production environments.
Step 1: Installing PM2
Let's begin our journey by installing PM2. Open your terminal and run:
npm install -g pm2
This command installs PM2 globally on your system, making it available for all your projects.
Step 2: Running Your First Node.js File with PM2
Let's start with a simple Node.js file. Create a file named app.js
with the following content:
console.log('Hello, PM2!');
setInterval(() => {
console.log("'I'm still running!");
}, 5000);
Now, let's run this file with PM2:
pm2 start app.js
Congratulations! You've just started your first process with PM2.
Step 3: Running npm Scripts with PM2
As your projects grow more complex, you'll often use npm scripts defined in your package.json
file to start your application. PM2 can manage these scripts just as easily as it manages individual files.
Let's use a Next.js application as an example:
- First, ensure your
package.json
has a start script. For a Next.js app, it typically looks like this:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
- To run the production version of your Next.js app with PM2, use the following command:
pm2 start npm --name "next-app" -- start
This tells PM2 to run the npm start
command, which in turn runs next start
for a Next.js application.
pm2 start npm
: Runs thenpm
command using PM2.--name "next-app"
: Names the process for easy identification.-- start
: Passesstart
as an argument tonpm
, ensuring thatnpm
runs thestart
script defined in yourpackage.json
.
- If you want to run the development version, you can use:
pm2 start npm --name "next-app-dev" -- run dev
This runs npm run dev
, which starts the Next.js development server.
- You can apply the same principle to other npm scripts. For example, if you have a custom script called
server
:
pm2 start npm --name "custom-server" -- run server
This approach is not limited to Next.js. You can use it with any Node.js application that uses npm scripts, such as Express.js servers, React applications, or even custom Node.js scripts.
Step 4: Managing Multiple npm Scripts
For complex projects with multiple components, such as a MERN (MongoDB, Express, React, Node.js) stack application, you'll often need to manage several npm scripts simultaneously. PM2's ecosystem file feature is perfect for this scenario, allowing you to define and manage multiple processes in a single configuration file.
Create a file named ecosystem.config.js
in your project root. This file will define all the processes PM2 should manage. Here's an expanded example:
module.exports = {
apps: [
{
name: "next-app",
script: "npm",
args: "start",
cwd: "./frontend",
env: {
NODE_ENV: "production",
PORT: 3000
},
env_development: {
NODE_ENV: "development",
PORT: 3000
}
},
{
name: "express-server",
script: "npm",
args: "run server",
cwd: "./backend",
env: {
NODE_ENV: "production",
PORT: 5000
},
env_development: {
NODE_ENV: "development",
PORT: 5000
}
},
]
}
Let's break down the configuration options:
name
: A unique identifier for each process.script
: The command to run. Use"npm"
for npm scripts.args
: Arguments to pass to the script. For npm scripts, use"start"
,"run server"
, etc.cwd
: The working directory for the process. Useful when your frontend and backend are in separate directories.env
: Environment variables for production.env_development
: Environment variables for development.
To start all processes defined in your ecosystem file:
pm2 start ecosystem.config.js
To start processes in development mode:
pm2 start ecosystem.config.js --env development
This approach gives you the flexibility to manage complex applications with multiple components, all through PM2.
Step 5: Basic PM2 Commands
Now that we have a process running, let's learn some basic PM2 commands:
- List running processes:
pm2 list
- Stop a specific process::
pm2 stop next-app
- Restart a specific process:
pm2 restart next-app
- Delete a specific process from PM2:
pm2 delete next-app
- Stop all processes:
pm2 stop all
- Restart all processes:
pm2 restart all
- Delete all processes:
pm2 delete all
Step 6: Monitoring Your Application
- PM2 provides a real-time monitoring tool. Try it out:
pm2 monit
This command opens an interface where you can see CPU usage, memory consumption, and logs for your running processes.
- Display process details:
pm2 show next-app
This command provides detailed information about a specific process, including its configuration, metadata, and recent logs.
Step 7: Reloading Without Downtime
To update your application with zero downtime:
pm2 reload ecosystem.config.js
This command gracefully reloads all processes defined in your ecosystem file, ensuring that your application remains available during updates.
Step 8: Managing Logs
Logs are crucial for understanding what's happening in your application. Here's how to view them:
- View logs:
pm2 logs
- View logs for a specific application:
pm2 logs next-app
- Clear log files:
pm2 flush
- To view only error logs:
pm2 logs --err
Step 9: Automatic Restart on Crashes
One of PM2's most powerful features is its ability to automatically restart applications that crash or stop unexpectedly. This feature ensures high availability and reliability for your Node.js applications.
Here's how it works:
PM2 constantly monitors the processes it manages.
If a process crashes or exits with an error code, PM2 will automatically restart it.
This happens without any manual intervention, minimizing downtime.
Here are some key options that allow you to fine-tune the automatic restart behavior. These options can be added to your ecosystem.config.js
file or specified when starting an application.
max_restarts
: Sets the maximum number of restarts within a time frame.min_uptime
: Minimum uptime of the app to be considered started.max_memory_restart
: Restarts the app if it exceeds a specified memory limit.restart_delay
: Delay between automatic restarts (in milliseconds).autorestart
: Enables or disables the automatic restart feature.
Let's update the ecosystem.config.js
file to include these options:
module.exports = {
apps: [{
name: "my-app",
script: "app.js",
max_restarts: 10,
min_uptime: "5s",
max_memory_restart: "200M",
restart_delay: 4000,
autorestart: true
}]
}
In this configuration:
The app will restart a maximum of 10 times.
The app must run for at least 5 seconds to be considered successfully started.
If the app uses more than 200MB of memory, it will be restarted.
There will be a 4-second delay between restarts.
Automatic restarts are enabled (this is the default, but we're explicitly setting it here).
You can also set these options when starting an app from the command line:
pm2 start app.js --max-restarts 10 --min-uptime 5000 --max-memory-restart 200M --restart-delay 4000
Example: Demonstrating Automatic Restart
Let's create a simple Node.js application that will crash periodically to demonstrate PM2's automatic restart feature.
Create a new file named
crash-test.js
with the following content:let count = 0; setInterval(() => { console.log(`Running for ${count} seconds`); count++; if (count % 10 === 0) { console.log("Simulating a crash..."); throw new Error("Application crashed!"); } }, 1000);
This script will run for 10 seconds and then simulate a crash by throwing an error.
Create an
ecosystem.config.js
file with the following content:module.exports = { apps: [{ name: "crash-test", script: "crash-test.js", max_restarts: 5, min_uptime: "3s", restart_delay: 2000 }] }
Start the application with PM2:
pm2 start ecosystem.config.js
Monitor the application:
pm2 monit
You'll see in the monitoring interface that every 10 seconds, the application crashes and PM2 automatically restarts it, with a 2-second delay between restarts.
To view the logs and see the crash and restart events:
pm2 logs crash-test
You'll see output similar to this:
0|crash-test | Running for 8 seconds 0|crash-test | Running for 9 seconds 0|crash-test | Simulating a crash... 0|crash-test | Error: Application crashed! 0|crash-test | at Timeout._onTimeout (/path/to/crash-test.js:8:11) 0|crash-test | at listOnTimeout (node:internal/timers:559:17) 0|crash-test | at processTimers (node:internal/timers:502:7) PM2 | App [crash-test:0] exited with code [1] via signal [SIGINT] PM2 | App [crash-test:0] starting in -fork mode- 0|crash-test | Running for 0 seconds 0|crash-test | Running for 1 seconds
This example demonstrates how PM2 automatically restarts your application when it crashes, ensuring continuous operation even in the face of unexpected errors. The configuration options we've set limit the number of restarts to 5 and introduce a 2-second delay between restarts, helping to prevent rapid restart loops in case of persistent issues.
Real-world Example: MERN Stack
Here's how you might set up an ecosystem file for a typical MERN stack application:
module.exports = {
apps: [
{
name: "react-frontend",
script: "npm",
args: "start",
cwd: "./client",
env: {
NODE_ENV: "production",
PORT: 3000
}
},
{
name: "express-backend",
script: "npm",
args: "run server",
cwd: "./server",
env: {
NODE_ENV: "production",
PORT: 5000,
MONGODB_URI: "mongodb://localhost:27017/myapp"
}
},
{
name: "mongodb",
script: "mongod",
args: "--port 27017"
}
]
}
This configuration starts a React frontend, an Express.js backend, and a MongoDB instance, all managed by PM2.
By leveraging PM2's ecosystem files, you can efficiently manage complex, multi-component applications, ensuring all parts of your stack start together and are monitored effectively. This approach simplifies deployment, improves maintainability, and gives you fine-grained control over each component of your application.
Conclusion
Congratulations! You've completed your journey from PM2 basics to advanced usage. You've learned how to start simple scripts, manage complex applications, handle logs, use environment variables, and even tackle a full MERN stack. Remember, PM2 is a powerful tool with many more features to explore. As you continue your development journey, don't hesitate to dive into the PM2 documentation for more advanced usage and optimizations.
Happy coding, and may your applications always stay up and running with PM2!