Table of Contents
URL: https://www.progressiverobot.com/how-to-use-pm2-to-setup-a-node-js-production-environment-on-an-ubuntu-vps/
Introduction
Running a Node.js application with node app.js is perfectly acceptable for development. However, this approach is not sustainable for a production environment. If the application crashes, it will not restart on its own. If the server reboots, the application will remain offline until it is manually started. This creates significant reliability problems.
To solve this, a process manager is necessary. PM2 is a production-grade process manager for Node.js applications that provides a simple and effective way to manage and daemonize applications. It keeps applications running indefinitely, reloads them during updates without downtime, and facilitates managing logs, clusters, and startup behavior.
This article will provide a complete guide to installing PM2 on an Ubuntu server, managing a Node.js application, and using its features to build a production-ready setup. We will cover installation, basic commands, security hardening, deployment automation, and advanced monitoring.
[info]
Key Takeaways:
- PM2 provides automatic crash recovery, startup script generation, and process monitoring to keep applications reliably online.
- PM2's cluster mode (
pm2 start app.js -i max) runs multiple instances across all available cores and load-balances traffic between them, dramatically improving throughput.
- Using
pm2 reloadinstead ofpm2 restartenables code updates without downtime. In cluster mode, PM2 restarts workers one at a time, ensuring at least one instance is always available to handle requests during deployments.
- To ensure applications restart after server reboots, you must run both
pm2 startup(which generates and registers asystemdservice) andpm2 save(which saves the current process list). Missing either step means apps won't auto-restart.
- The
ecosystem.config.jsfile replaces unwieldy command-line flags with a clean JavaScript configuration. It manages multiple apps, environment variables, cluster settings, and deployment environments in one maintainable location.
- PM2 automatically captures application logs, but they grow indefinitely by default. Installing
pm2 install pm2-logrotateenables automatic log rotation and retention policies, preventing logs from consuming all available disk space over time.
- PM2 and Docker solve different problems: PM2 manages Node.js processes while Docker provides environment isolation. The best practice for containerized deployments is using
pm2-runtimeinside Docker containers to get both container portability and advanced Node.js process management features.
Prerequisites
Before you begin, you will need the following:
- An Ubuntu server (this guide was tested with 25.04, but any recent version should work).
- A non-root user with
sudoprivileges.
- Node.js and npm installed. Using
nvm(Node Version Manager) is strongly recommended as it avoids permission issues. An active LTS release is best.
- A sample Node.js application. We will create a simple one for testing.
- A configured firewall (such as UFW) is recommended for production. We will cover basic setup in this guide.
What is PM2?
PM2 is a daemon process manager for Node.js that helps you manage and keep your application online. A "daemon" is a program that runs as a background process, rather than being under the direct control of an interactive user.
PM2's main features include:
- Process Monitoring: Automatically restarts applications if they crash or are killed.
- Startup Scripts: Generates scripts to launch PM2 and your applications on server boot.
- Cluster Mode: Allows you to run multiple instances of your application, load-balancing traffic across them to take advantage of multi-core CPUs.
- Log Management: Collects and centralizes logs from all running applications.
- Zero-Downtime Reloads: Updates applications with new code without losing active connections.
It provides a robust set of features specifically for Node.js applications, making it simpler than configuring systemd from scratch and more feature-rich than simpler tools like forever.
Installing PM2
PM2 is available as an npm package and should be installed globally so it can be accessed anywhere on the system.
A common mistake is to install global npm packages using sudo npm install. This is not recommended as it can cause permission issues and security problems. The best approach is to use a Node Version Manager (nvm), which installs Node.js and npm in your user's home directory, removing the need for sudo for global packages.
If you have nvm installed, you can install PM2 without sudo:
npm install pm2 -g
If the command above fails with an EACCES (permission denied) error, do not use sudo. Instead, install nvm first, then run the command again in a new terminal session.
Once installed, you can verify the installation by checking its version:
pm2 --version
Creating an Application
First, let's create a simple "Hello World" application using Express.js, a common Node.js web framework.
- Create a new directory for your project and navigate into it:
mkdir my-app
cd my-app
- Initialize a new Node.js project and install Express:
npm init -y
npm install express
- Create a file named
app.jswith your preferred text editor:
nano app.js
- Add the following code to
app.js. This creates a basic web server that listens on port3000.
// app.js
const express = require('express');
const app = express();
const PORT = 3000;
app.get('/', (req, res) => {
res.send('Hello from PM2!');
});
// A simple handler for a graceful shutdown
process.on('SIGINT', () => {
console.log('Received SIGINT. Shutting down gracefully.');
// Close server, database connections, etc.
process.exit(0);
});
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
Note: We added a SIGINT handler, which will be important for graceful reloads later.
- Save and close the file.
Instead of running node app.js, you can now start the application using PM2:
pm2 start app.js
PM2 will start the application in the background and output a table with process information:
[PM2] Spawning PM2 daemon with pm2_home=/home/user/.pm2
[PM2] PM2 successfully daemonized
[PM2] Starting /home/user/my-app/app.js in fork_mode (1 instance)
[PM2] Done.
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name │ mode │ pid │ status │ restart │ uptime │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0 │ app │ fork. │ 12345│ online │ 0 │ 0s │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
Your application is now running as a background process. You can test this using curl:
curl http://localhost:3000
You should see the output: Hello from PM2!
Basic PM2 Commands
PM2 provides a simple set of commands for managing your processes. Here are the most common ones:
| Command | Description |
| ———————– | ————————————————— |
pm2 start <script> |
Starts the application. |
pm2 list |
Lists all applications managed by PM2. |
| `pm2 stop <id\ | name>` | Stops a specific application. |
| `pm2 restart <id\ | name>` | Restarts a specific application. |
| `pm2 info <id\ | name>` | Displays detailed information about a process. |
| `pm2 logs <id\ | name>` | Shows real-time logs for a specific application. |
pm2 monit |
Opens a terminal-based dashboard for monitoring. |
| `pm2 delete <id\ | name>` | Stops and removes an application from the PM2 list. |
Note on id vs. name: When running in cluster mode, multiple processes will share the same name. Running a command like pm2 stop app will affect *all* processes in that cluster. To manage a single specific instance, you must use its unique id.
Managing Processes with PM2
Managing a single process is simple, but production environments require more, including scaling across CPU cores and automatic restarts on server boot.
Cluster Mode
On a server with multiple CPU cores, running a single Node.js process is inefficient. The Cluster Mode starts multiple instances (workers) of your application and load-balances incoming traffic between them.
To start app.js in cluster mode, use the -i (instances) flag. You can specify a number, or use max to automatically detect the number of available CPU cores:
# Start the app using all available cores
pm2 start app.js -i max
If you run pm2 list, you will now see multiple instances of your app running, each with a separate pid but sharing the same name.
Zero-Downtime Reloads
When you need to update your application's code, pm2 restart is one option, but it stops and then starts the application, which causes a brief period of downtime.
A better method is pm2 reload. This command is a key feature of cluster mode and provides a graceful, zero-downtime update. It works by restarting each worker one by one, waiting for the new worker to be online before taking the old one down. This ensures that there is always at least one worker available to handle requests.
pm2 reload app
This process works by sending a SIGINT signal to the old process. For a truly graceful shutdown, your Node.js application should listen for this signal (like process.on('SIGINT', ...) as shown in our example). This allows it to finish in-progress requests and clean up resources before exiting.
Important: If you use pm2 reload on an application running in fork mode (a single instance), it will simply fall back to the behavior of pm2 restart, which *will* cause downtime.
Saving the Process List for Server Reboots
A critical step is ensuring your applications restart when the server reboots. PM2 can generate a startup script that does this.
- Run the
startupcommand:
pm2 startup
- PM2 will output a command specific to your system (usually using
systemdon modern Ubuntu). You must copy and run this command:
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/home/sammy/.nvm/versions/node/v22.21.0/bin /home/sammy/.nvm/versions/node/v22.21.0/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy
*(Your path will vary depending on your Node.js version and username.)*
- Run the command that PM2 provides. This creates a
systemdunit that will launch PM2 on boot.
- Finally, save your current process list. PM2 will remember these applications and restart them when it launches:
pm2 save
Now, your applications will automatically restart after a server reboot.
Configuring PM2
Managing applications with command-line flags becomes difficult as options grow. PM2's preferred method is an ecosystem file. This is a JavaScript configuration file, typically named ecosystem.config.js, that defines all settings for one or more applications.
- Generate a template file:
pm2 ecosystem
- This creates a template
ecosystem.config.jsfile. Here's an example configured for our application:
// ecosystem.config.js
module.exports = {
apps : [{
name : "my-app",
script : "./app.js",
instances: "max",
exec_mode: "cluster",
watch: false, // Disable watch mode for production
env_production: {
NODE_ENV: "production",
PORT: 3000
},
env_development: {
NODE_ENV: "development",
PORT: 3001
}
}]
}
This file defines an application named "my-app", specifies its script, and sets it to run in cluster mode using all available CPU cores. It also defines different environment variables for production and development.
- To start this application, simply run:
pm2 start ecosystem.config.js
- To start it specifically using the production environment:
pm2 start ecosystem.config.js --env production
- If you change environment variables in the file for an already-running app, you must use the
--update-envflag withreloadorrestart:
pm2 reload ecosystem.config.js --env production --update-env
Monitoring and Debugging
PM2 provides several tools for monitoring and debugging your application, from built-in command-line tools to a sophisticated cloud-based dashboard.
Command-Line Monitoring
If your application doesn't work as intended, you can use the following commands to debug:
pm2 list: This is your first stop. Check thestatuscolumn. If it'serrored, the app is crashing. Check therestartcount. A high, climbing number indicates a crash loop.
pm2 logs app: This command tails the application's logs in real-time. This is the most effective way to see application errors. PM2 splits logs into standard output (app-out.log) and standard error (app-error.log).
pm2 monit: This launches a terminal-based dashboard showing real-time CPU and memory usage for each process.
pm2 info app: This provides a detailed overview, including the application's file paths, environment variables, and log file locations.
By default, PM2 log files will grow indefinitely. For a production server, this can eventually fill up your disk space. PM2 provides a module for managing log rotation. pm2-logrotate automatically "rotates" the log files when they reach a certain size or time. It renames the large log file (e.g., to app-out-1.log) and creates a new, empty one for the application to continue writing to. It then enforces a retention policy by automatically deleting the oldest log files, ensuring your server's disk never fills up. You can install it with: pm2 install pm2-logrotate.
Cloud-Based Monitoring (PM2 Plus)
For more advanced, cross-server monitoring, PM2 offers PM2 Plus (also known as Keymetrics). This is a cloud-based dashboard that provides:
- A centralized web interface for all your servers.
- Real-time metrics on CPU, memory, and application performance.
- Issue and exception tracking.
- Transaction tracing to identify performance bottlenecks.
- Alerting via Slack, email, and other services.
To connect your application, you run a simple command:
pm2 link <secret_key> <public_key>
This links your local PM2 daemon to the web dashboard, providing a comprehensive view of your application's health without requiring additional server setup.
Security Considerations and Best Practices
When running a production server, security is a primary concern. A single-line of defense, such as a simple password, is not enough. A multi-layered approach is the best practice. By implementing multiple, overlapping layers of security, you create a robust system where an attacker who breaches one layer is immediately confronted by another. For a PM2 and Node.js setup, this means securing the network, the server, the application gateway, and the application code itself.
Configuring a Basic Firewall (UFW)
The ufw (Uncomplicated Firewall) utility is a standard, easy-to-use firewall for Ubuntu. You should only allow traffic to the specific ports you need.
- Allow SSH: This is essential so you do not lock yourself out of the server.
sudo ufw allow OpenSSH
- Allow HTTP/HTTPS: If you are using a reverse proxy like Nginx (recommended), you should allow traffic on ports
80and443.
sudo ufw allow 'Nginx Full'
- Deny Other Ports: Your Node.js application port (e.g.,
3000) should *not* be publicly accessible. The firewall should block it by default, and only Nginx (running on the same server) should be able to access it.
- Enable UFW:
sudo ufw enable
This configuration ensures that the public can only access your web server, which then securely forwards requests to your PM2-managed application.
Running as a Non-Root User
A fundamental security principle is the principle of least privilege. This means any application or user should only have the bare minimum permissions necessary to perform its function.
You should never run your Node.js application as the root user. If an attacker found a vulnerability in your application, they could gain root access to your entire server.
Instead, create a dedicated non-privileged user for your application. When you run the pm2 startup command, use the -u flag to specify this user, as shown in the *Saving the Process List* section. This user should not have sudo permissions. This way, even if your application is compromised, the attacker's access is limited to that user's home directory and permissions.
Using a Reverse Proxy for Security
- Abstraction: The public never directly accesses your Node.js application. This hides details about your application stack and prevents direct attacks against the Node.js process.
- SSL Termination: The reverse proxy can handle all HTTPS and SSL certificate complexity. This encrypts all traffic between the user and your server. Your Node.js application can run on a simple HTTP port locally, simplifying your code.
- Attack Mitigation: A robust web server like Nginx is built to handle the public internet. It can be configured for rate limiting (to prevent brute-force attacks), blocking malicious IPs, and mitigating common web vulnerabilities.
- Static Content: The proxy can efficiently serve static files (CSS, JavaScript, images), reducing the load on your Node.js application, which is better saved for dynamic requests.
Protecting Environment Variables
Your application will inevitably need "secrets" like database passwords, API keys, or session tokens. These must never be hardcoded into your application's source code (e.g., const DB_PASS = "mypassword123"). If your code is in a Git repository, you will have permanently leaked that secret.
These secrets should be passed to your application using environment variables. PM2's ecosystem.config.js file allows you to define these in an env_production block. However, if you commit this file to source control, you still have the same problem.
Here are the two recommended methods:
- Using a
.envFile: Create a file named.envin your project's root directory. Place your secrets here:
DB_HOST=localhost
DB_PASS=mypassword123
API_KEY=supersecretkey
Add .env to your .gitignore file to ensure it is never committed. Then, in your application, use a package like dotenv (npm install dotenv) to load these variables.
- Injecting During Deployment: This is a more secure method for CI/CD pipelines. Your secrets are stored securely in your CI/CD tool (e.g., GitHub Actions Secrets, GitLab CI/CD Variables). During the deployment step, the pipeline securely logs into your server and passes the variables directly to the PM2 command:
API_KEY=$CI_SECRET_KEY DB_PASS=$CI_DB_PASS pm2 reload ecosystem.config.js --env production
This way, the secrets never touch a file on the server, existing only as process environment variables.
Disable Watch Mode in Production
PM2 has a watch mode that automatically restarts your application when it detects file changes. This is a convenient feature for development. In production, it is a liability.
You must disable watch mode because:
- Unintended Restarts: Many things can write files to your application directory, including log files or temporary cache files. This can trigger an unwanted application restart.
- Resource Intensive: Continuously watching the filesystem consumes unnecessary CPU and I/O resources.
- Not a Deployment Strategy: This is not a proper way to deploy code. A real deployment should be a deliberate, controlled event, usually involving a CI/CD pipeline, dependency installation, and a graceful
pm2 reload.
Explicitly set watch: false in your ecosystem file to ensure it is disabled:
// ecosystem.config.js
module.exports = {
apps : [{
name : "my-app",
script : "./app.js",
watch: false, // Explicitly disable for production
...
}]
}
Common Issues and Troubleshooting
When working with PM2 in a production environment, you may encounter a few common issues. This section provides detailed explanations and solutions for them.
Application Status is errored or in a Crash Loop
- Problem: You run
pm2 listand see your application's status iserroredinstead ofonline. Therestartcount for that process may also be very high and climbing.
- Cause:
erroredstatus means PM2 is successfully starting your application, but the application is crashing almost immediately. PM2, doing its job, tries to restart it, leading to a "crash loop." This is almost always caused by an error in your application code, not PM2.
- Solution: You must read the application's logs to find the error.
- Use the
pm2 logscommand to view the error in real-time. Replaceappwith your application's name or ID:
pm2 logs app --lines 100
- Look for common startup errors, such as:
ReferenceErrororTypeError: A variable is not defined or a function is misused.
Error: EADDRINUSE :::3000: The port your app is trying to use (e.g.,3000) is already in use by another service.
- Database connection errors: The application cannot reach the database due to wrong credentials or a down service.
- Module not found: You ran
npm installbut forgot a required package.
- You can also find the full log file path by running
pm2 info appand checking theerror log path.
pm2: command not found After Server Reboot
- Problem: You reboot the server, and your applications are not running. When you log in and type
pm2 list, you get abash: pm2: command not founderror.
- Cause: This almost always happens when you have installed Node.js and PM2 using
nvm(Node Version Manager). Thepm2 startupcommand creates asystemdservice that runs on boot, but the system'srootuser does not know where yournvm-installedpm2executable is. ThePATHenvironment variable is not set correctly for the boot-time service.
- Solution: You must run the
pm2 startupcommand and copy the *exact* command it outputs. This output includes the necessaryPATHenvironment variable.
- If you have a broken startup script, remove it first:
pm2 unstartup
- Run the
startupcommand again:
pm2 startup
- Carefully copy the full command PM2 gives you. It will look something like this, with the
env PATH=$PATH...part being the most important:
sudo env PATH=$PATH:/home/sammy/.nvm/versions/node/v22.21.0/bin /home/sammy/.nvm/versions/node/v22.21.0/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy
- Run this
sudocommand. This correctly registers thesystemdservice with the correct path topm2.
- Finally, run
pm2 saveto store your current process list.
Applications Do Not Restart on Reboot (but pm2 command works)
- Problem: You reboot the server. When you log in,
pm2 listworks, but the list of applications is empty. Your apps are not running.
- Cause: PM2's startup process is two-fold:
pm2 startupcreates asystemdservice that launches the PM2 daemon on boot.
pm2 savesaves your current list of running processes to a file on disk (e.g.,~/.pm2/dump.pm2). When PM2 starts on boot, it loads this file to know *what* to run. You most likely forgot to runpm2 saveafter starting your applications.
- Solution:
- Start all your applications as you normally would:
pm2 start ecosystem.config.js --env production
- Verify they are
onlinewithpm2 list.
- Run
pm2 saveto write this process list to disk.
pm2 save
- You can test this by running
sudo rebootand checkingpm2 listafter the server is back online.
Deployed Code Changes Are Not Reflected
- Problem: You pulled new code from your Git repository and ran
npm install, but your application is still serving the old code.
- Cause: PM2 does not automatically detect file changes *unless* you explicitly enable
watchmode. Watch mode is not recommended for production as it is resource-intensive and can lead to unintended restarts. You must manually tell PM2 to update the running processes with the new code.
- Solution: Do not use
pm2 restart. This command causes downtime. Instead, usepm2 reload:
pm2 reload ecosystem.config.js --env production
Or, if you are not using an ecosystem file:
pm2 reload app
The reload command performs a zero-downtime reload in cluster mode. It restarts each worker instance one by one, ensuring at least one worker is always available to handle traffic.
High CPU (100%) or Memory Usage
- Problem: Your server becomes slow, and checking with
toporhtopshows a Node.js or PM2 process at 100% CPU, or its memory usage is climbing steadily.
- Cause:
- 100% CPU: This is often a symptom of a crash loop, as described in the first issue. The application crashes and restarts so quickly that it consumes a full CPU core.
- High Memory: This typically indicates a memory leak in your Node.js application. Your code is creating objects, adding listeners, or holding references that the garbage collector cannot clean up, causing memory to grow until the process crashes (and is restarted by PM2).
- Solution:
- Run
pm2 monitto get a real-time dashboard of CPU and memory usage for each process.
- If CPU is at 100%, check the logs immediately with
pm2 logs app. A fast-scrolling error log confirms a crash loop.
- If memory is climbing steadily, this is a code-level memory leak. PM2 is keeping your app *alive*, but it cannot *fix* the leak. You will need to use Node.js debugging tools like the built-in
--inspectflag or external services to profile your application's memory heap and find the source of the leak.
Comparing PM2 with Other Tools
Choosing the right tool to run your Node.js application in production is a critical decision. PM2 is a powerful and popular choice, but it is not the only option. It exists in an ecosystem of tools that includes native operating system services, simpler process runners, and full-blown containerization platforms.
The best choice depends on your project's specific needs, scale, and complexity. This section provides a detailed comparison of PM2 with its main alternatives: systemd, forever, and Docker.
PM2 vs. systemd
systemd is the default init system and service manager for most modern Linux distributions, including Ubuntu. It is responsible for starting, stopping, and managing all system services (like the SSH server, network, and database).
- Pros:
- Built-in and Robust: As a native part of the OS,
systemdis extremely stable, efficient, and has virtually zero performance overhead.
- Language-Agnostic: It can manage any executable, not just Node.js applications.
- Service Dependencies: You can configure
systemdto start your Node.js application *only after* the database service (like PostgreSQL or MySQL) is online.
- Cons:
- Verbose Configuration: Setting up a service requires writing a
.serviceunit file. This is more complex and less intuitive than a PM2 command or ecosystem file.
- Not "Node-Aware":
systemdsees your application as just another process. It has no built-in understanding of Node.js. Critical features like cluster mode (to utilize all CPU cores) and zero-downtime reloads must be manually scripted, which is a significant and complex task.
For simple applications on a single server, systemd is a viable, lightweight option. However, it lacks the Node.js-specific features that PM2 provides out of the box, making PM2 a more practical and feature-rich choice for Node.js developers.
PM2 vs. forever
forever is one of the original process managers for Node.js. Like PM2, its basic purpose is to keep a Node.js script running.
- Pros:
- Simple and Lightweight: It is very easy to use for its one task: restarting a script if it crashes.
- Cons:
- Lacks Production Features:
foreverhas been largely superseded by PM2 because it is missing nearly every key feature needed for a modern production environment. It has:
- No built-in cluster mode.
- No zero-downtime reload.
- No startup script generation (you must manually hook it into
crontaborsystemd).
- No ecosystem configuration file for managing multiple apps.
forever is a tool from a previous generation. For any serious production application, PM2 is the superior choice.
PM2 vs. Docker
This is the most common point of comparison, and it's important to understand they solve different problems. Docker is a containerization platform, not just a process manager. It packages your application, its dependencies, its runtime (Node.js), and parts of the operating system into a single, isolated "container."
- Pros:
- Full Environment Isolation: The primary benefit. A container runs identically on your laptop, a testing server, and in production, eliminating "it works on my machine" problems.
- Portability and Scalability: Containers can be easily moved and scaled across many servers using an orchestrator like Kubernetes.
- Cons:
- Higher Complexity: Docker introduces a new layer of abstraction, including virtual networks, storage volumes, and image management.
- Process Management: By default, Docker only manages the container itself. If the
node app.jscommand (PID 1) inside the container crashes, the container stops and restarts. It does not provide built-in clustering or graceful reloads.
When to Choose PM2 vs. Docker
- Choose PM2 when: Your goal is to run a Node.js application on a traditional Ubuntu server (a virtual machine or bare metal). It provides the simplest and fastest path to a production-ready setup with process monitoring, clustering, and log management. It is ideal for monolithic applications, small microservices, or any project where the complexity of container orchestration is not (yet) required.
- Choose Docker when: Your primary need is environment consistency and portability. If you have a complex microservice architecture, need to ensure development and production environments are identical, or plan to deploy to a cloud platform that uses Kubernetes, Docker is the right choice.
- The "Both" Strategy (Recommended for Docker Users): A very common and powerful pattern is to **use PM2 *inside* a Docker container Instead of running
node app.jsas your container'sCMD, you usepm2-runtime ecosystem.config.js. This gives you the best of both worlds. Docker provides the environment isolation and portability. PM2** manages the Node.js process *inside* the container, giving you cluster mode (to use all CPU cores *allocated to the container*) and zero-downtime reloads. Thepm2-runtimecommand is specifically designed to run PM2 in the foreground, which is what Docker's container lifecycle requires.
Performance Benchmarks and Overhead
While exact performance benchmarks are application-specific, we can compare the general overhead and impact of each tool:
systemd: Has almost no measurable overhead, as it is a native OS component.
forever: Has minimal overhead, as it is a simple Node.js script.
PM2: The PM2 daemon itself has a very small, negligible memory and CPU footprint. Its most significant performance impact is positive. The cluster mode feature allows your single-threaded Node.js application to utilize all CPU cores on your server, dramatically improving application throughput. This benefit far outweighs the small overhead of the daemon.
Docker: Introduces a small but measurable overhead from the containerization layer (virtualized networking, filesystem). This is minimal on modern Linux systems but is still greater than running a process directly on the host.
Comparison Summary Table
| Feature | PM2 | systemd |
Docker |
forever |
| ————————- | ———————————— | ——————————- | ———————————– | ————————– |
| Primary Use | Node.js Process Management | System-wide Service Management | Environment Containerization. | Simple Process Runner |
| Cluster Mode | Yes (Built-in) | No (Requires manual scripting) | No (Manages container, not process) | No |
| Zero-Downtime Reload | Yes (Built-in) | No (Requires manual scripting) | No (Restarts entire container) | No |
| Startup Script | Yes (Generates systemd script) |
Yes (Is the startup system) | No (Managed by orchestrator) | No (Requires manual setup) |
| Environment Isolation | No | No | Yes (Full OS-level isolation) | No |
| "Node-Aware" | Yes | No | No | Yes |
| Best For | Node.js apps on a VM | System-level services | Portability & Microservices | Very simple scripts |
CI/CD and Deployment Automation
PM2 is easily integrated into an automated deployment pipeline (CI/CD). Instead of manually running git pull and pm2 reload on your server, you can use tools like GitHub Actions or Jenkins.
A typical deployment script in your CI/CD pipeline might look like this:
#!/bin/bash
# deploy.sh
# 1. Navigate to the application directory
cd /var/www/my-app
# 2. Pull the latest code from the main branch
git pull origin main
# 3. Install or update dependencies
npm install --production
# 4. Reload the application with PM2
# This will perform a zero-downtime reload
pm2 reload ecosystem.config.js --env production
In your GitHub Actions workflow, you would have a "Deploy" step that uses SSH to run this deploy.sh script on your server after your tests have passed. The pm2 reload command is the key, as it updates the application without any downtime for your users.
FAQs
1. What is PM2 used for in Node.js?
PM2 is a production process manager for Node.js. Its main purpose is to run your Node.js application in a stable and reliable way, solving the problems that node app.js does not.
It wraps your application and provides a set of essential features:
- Process Monitoring: It automatically restarts your application if it crashes due to an error.
- Startup Scripts: It ensures your application restarts automatically if the entire server reboots.
- Cluster Mode: It allows your application to use all available CPU cores, dramatically improving performance.
- Log Management: It captures all your application's logs (
console.log,console.error) and saves them to files.
- Zero-Downtime Reloads: It lets you update your application's code without any downtime for your users.
2. How do I auto-start my Node.js app on Ubuntu reboot?
You use a two-step process that involves the startup and save commands.
pm2 startup: First, you run this command. PM2 will detect your system's init system (likesystemdon Ubuntu) and give you asudocommand to run. You must copy and paste this command. This registers the PM2 *daemon itself* as a system service.
pm2 save: Once the daemon is set to start on boot, you must tell it *which applications* to run. This command saves your currently running process list to a file.
After you do both, when your server reboots, systemd will start PM2, and PM2 will read its saved list and relaunch all your applications.
3. How does PM2 compare to Forever.js or Docker?
- vs. Forever.js:
foreveris a simpler, older tool that only restarts a script if it crashes. PM2 is a complete replacement forforever, adding critical production features thatforeverlacks, such as a built-in cluster mode, zero-downtime reloads, and automatic startup script generation.
- vs. Docker: They solve different problems. Docker is a containerization platform that isolates your app and its entire environment (Node.js, libraries, etc.). PM2 is a process manager that runs *inside* an environment. The best practice is often to use them together: you run PM2 (using
pm2-runtime) inside a Docker container to get both environment isolation (from Docker) and Node.js process management (from PM2).
4. Can I monitor my Node.js apps remotely with PM2?
Yes. PM2 offers a cloud-based service called PM2 Plus (also known as Keymetrics).
You run a pm2 link command on your server to connect it to the PM2 Plus web dashboard. This gives you a centralized, remote interface where you can monitor real-time CPU and memory usage, view logs, and set up alerts for crashes or performance issues from any machine. This is separate from the built-in CLI commands like pm2 monit, which only work on the server itself.
5. How do I use PM2 cluster mode for better performance?
You use PM2's cluster mode to run multiple instances of your application, which is the best way to improve its performance on a multi-core server.
Why: A standard Node.js application is single-threaded, meaning it only uses one CPU core, even if your server has 8 or 16. The other cores sit idle.
How: PM2's cluster mode starts one instance of your app on each CPU core and automatically load-balances incoming traffic between them.
You can enable it in two ways:
- Via the CLI: Use the
-i maxflag to automatically detect and use all available cores.
pm2 start app.js -i max
- Via an Ecosystem File (Recommended): In your
ecosystem.config.js, set these two properties:
...
instances: "max",
exec_mode: "cluster",
...
Conclusion
PM2 provides a simple and feature-rich solution for running Node.js applications in a production environment. It solves the fundamental problems of process monitoring, restarts, and scaling that are not addressed by simply running node app.js.
By using PM2's cluster mode and startup scripts, you can ensure your application is resilient and utilizes your server's hardware efficiently. When combined with an ecosystem configuration file, a reverse proxy like Nginx, and automated deployment scripts, PM2 forms the foundation of a robust and maintainable production setup.
For more Node.js-related tutorials, check out the following articles: