Skip to main content
Lead Generation Websites, Google Maps Ranking, WhatsApp Funnels, Ecommerce, SEO, Web DesignSpeed Optimization · Conversion Optimization · Monthly Lead Systems · AI AutomationLead Generation Websites, Google Maps Ranking, WhatsApp Funnels, Ecommerce, SEO, Web Design

Deploy Node.js on a Linux Server with Nginx and PM2 (Beginner Deployment Guide)

Published: December 20, 2025
Written by Sumeet Shroff
Deploy Node.js on a Linux Server with Nginx and PM2 (Beginner Deployment Guide)
Table of Contents
  1. Introduction: Why Deploy Node.js on Linux with Nginx and PM2?
  2. What You'll Achieve
  3. Further Reading
  4. Understanding the Deployment Stack: Node.js, Nginx, PM2, and Linux
  5. The Building Blocks
  6. 1. Linux Server: The Foundation
  7. 2. Node.js: The Runtime
  8. 3. Nginx: The Reverse Proxy
  9. 4. PM2: The Process Manager
  10. How Do These Components Work Together?
  11. Visualizing the Stack
  12. Why This Stack?
  13. When Would You Use Something Else?
  14. Further Reading
  15. Preparing Your Linux Server (Ubuntu & CentOS)
  16. 1. Choose and Provision Your Server
  17. 2. Connect Securely via SSH
  18. Optional: Set Up SSH Keys (Highly Recommended)
  19. 3. Update Your Server’s Packages
  20. 4. Install Basic Utilities (Optional but Helpful)
  21. Quick Checklist
  22. Further Reading
  23. Installing Node.js on Linux (Ubuntu & CentOS)
  24. 1. Why Use Official Sources or nvm?
  25. 2. Installing Node.js on Ubuntu (with NodeSource)
  26. 3. Installing Node.js on CentOS (with NodeSource)
  27. 4. (Recommended) Managing Node.js Versions with nvm
  28. When to Use nvm vs. System Packages
  29. 5. Troubleshooting Common Installation Issues
  30. Quick Checklist
  31. Further Reading
  32. Uploading and Setting Up Your Node.js Application
  33. 1. Transfer Your Application Files
  34. Option A: Using scp (Secure Copy)
  35. Option B: Using SFTP (GUI Tools)
  36. Option C: Clone from Git
  37. 2. Organize Your Application Directory
  38. 3. Install Dependencies
  39. 4. Set Environment Variables for Production
  40. 5. Set File & Directory Permissions
  41. Quick Checklist
  42. Mini Project: Deploy a Test App
  43. Further Reading
  44. Installing and Configuring PM2 for Node.js Process Management
  45. 1. Install PM2 Globally
  46. 2. Start and Monitor Your Node.js App with PM2
  47. 3. Configure Automatic Restarts (on Crash or Reboot)
  48. a. Generate a Startup Script
  49. b. Save Your Process List
  50. c. Reboot Test
  51. 4. Access Logs and Check Process Status
  52. Mini-Project: Deploy a Sample App with PM2
  53. Common PM2 Tasks Cheat Sheet
  54. Further Reading
  55. Installing and Configuring Nginx as a Reverse Proxy for Node.js
  56. 1. Install Nginx on Your Server
  57. Ubuntu (20.04/22.04+)
  58. CentOS (7/8/Stream)
  59. 2. Write a Basic Reverse Proxy Configuration
  60. a. Find Your Node.js App’s Internal Port
  61. b. Create a New Nginx Server Block (Ubuntu: /etc/nginx/sites-available/yourapp)
  62. c. Firewall Settings
  63. 3. Test Nginx and Node.js Integration
  64. 4. Troubleshooting Nginx-Node.js Setups
  65. Mini-Checklist: Basic Nginx Reverse Proxy for Node.js
  66. Further Reading
  67. Securing Your Node.js Deployment: Basic Best Practices
  68. 1. Configure a Firewall (UFW or firewalld)
  69. Ubuntu (UFW)
  70. CentOS (firewalld)
  71. 2. Set Up HTTPS with Let’s Encrypt (Free SSL)
  72. a. Install Certbot
  73. b. Obtain and Install a Certificate
  74. 3. Harden Nginx and Node.js Settings
  75. 4. Linux Permissions and User Roles Basics
  76. Mini-Project: Test Your Security
  77. Further Reading
  78. Testing and Verifying Your Production Node.js Deployment
  79. 1. Test Your Node.js App Through Nginx from a Browser
  80. 2. Check Logs for Node.js, PM2, and Nginx
  81. 3. Simulate Traffic to Verify Stability
  82. 4. Identify and Resolve Common Deployment Errors
  83. Mini-Checklist: Production Deployment Works!
  84. What’s Next?
  85. Further Reading
  86. Automating Node.js Deployment with PM2 Ecosystem File
  87. 1. Create a PM2 Ecosystem File
  88. 2. Customize Your ecosystem.config.js
  89. 3. Managing Multiple Node.js Apps
  90. 4. Running and Managing Apps with PM2
  91. 5. Automate Restarts and Updates
  92. 6. Environment Variables for Production
  93. 7. Micro-Project: Version Control Your Ecosystem File
  94. 8. Recap
  95. Further Reading
  96. Best Practices for Node.js Production Deployment on Linux
  97. 1. Security Best Practices
  98. 2. Performance Tuning
  99. 3. Monitoring and Logging
  100. 4. Backups and Disaster Recovery
  101. 5. Ongoing Maintenance
  102. Micro-Checklist: Node.js Production Deployment on Linux
  103. Further Reading
  104. Troubleshooting Common Node.js Deployment Issues
  105. 1. Diagnosing Nginx and Node.js Integration Issues
  106. 2. Resolving PM2 Process and Log Errors
  107. 3. Addressing Firewall and Port Conflicts
  108. 4. Fixing Permission and Ownership Problems
  109. Case Checklist: General Troubleshooting
  110. Further Reading
  111. Case Study: Deploying Multiple Node.js Apps on One Server
  112. 1. Prepare Your Project Structure
  113. 2. Create a Unified PM2 Ecosystem File
  114. 3. Configure Nginx for Multiple Domains
  115. 4. Set Up Domains and DNS
  116. 5. Monitor and Control Multiple Apps with PM2
  117. 6. Avoiding Common Pitfalls
  118. Mini-Project: Add a Third App
  119. Further Reading
  120. Conclusion and Next Steps
  121. Where to Go Next
  122. Further Reading
  123. About Prateeksha Web Design

Introduction: Why Deploy Node.js on Linux with Nginx and PM2?

Deploying your Node.js application on a Linux server using Nginx and PM2 is a common, production-ready approach embraced by startups and enterprises alike. This stack leverages the strengths of each technology: Linux’s stability, Node.js’s speed, Nginx’s power as a reverse proxy, and PM2’s robust process management. If you’re aiming to take your Node.js project from development to the real world, mastering this deployment workflow is essential.

In this beginner-friendly deployment guide, you’ll learn how to “deploy nodejs on linux server” step by step. We’ll focus on practical, actionable instructions suitable for developers new to production deployments—but with enough detail to give you confidence for real-world scenarios.

By the end of this guide, you’ll understand why so many teams choose this stack and what advantages it brings:

  • Linux is the most popular OS for servers, known for its security, efficiency, and flexibility.
  • Node.js allows you to build fast, scalable backend applications in JavaScript.
  • Nginx acts as a high-performance reverse proxy and load balancer, shielding your app and improving reliability.
  • PM2 ensures your Node.js app stays running, even after crashes or reboots.

What You'll Achieve

  • Grasp the advantages of deploying on Linux (Ubuntu and CentOS)
  • Learn the roles and interplay of Node.js, Nginx, and PM2
  • Identify each stage necessary for a smooth deployment
  • Set realistic expectations for your first live Node.js launch
Fact Over 90% of cloud servers run on a Linux-based operating system, making Linux the industry standard for server deployments.

Whether you’re launching a new SaaS, a personal site, or a client project, this guide will help you:

  • Avoid common pitfalls in Node.js deployments
  • Secure your application and server
  • Use industry-standard tools for reliability and performance

Ready to dive in? Let’s break down each component of our deployment stack and see how they fit together.

Further Reading


Understanding the Deployment Stack: Node.js, Nginx, PM2, and Linux

Before you start running commands on your server, it’s crucial to understand how each piece of the deployment puzzle fits together. This section provides a clear overview of what each technology does, why it matters, and how data flows from a web browser to your Node.js application and back.

The Building Blocks

1. Linux Server: The Foundation

Linux is the operating system powering most cloud infrastructure. It’s known for its stability, performance, and extensive community support. Both Ubuntu and CentOS are popular distributions for Node.js deployment:

  • Ubuntu: User-friendly, widely supported, and a common choice for cloud VMs.
  • CentOS: Enterprise-focused, stable, and a favorite for long-term support scenarios.

2. Node.js: The Runtime

Node.js allows you to run JavaScript code on the server, building backend APIs, web servers, or real-time apps. When you deploy Node.js on a Linux server, you’re leveraging a fast, event-driven runtime that can handle thousands of connections efficiently.

3. Nginx: The Reverse Proxy

Nginx is a high-performance web server and reverse proxy. In a production stack, Nginx sits in front of your Node.js app, handling incoming HTTP/HTTPS requests. Nginx can:

  • Route traffic to your Node.js app (or multiple apps)
  • Serve static files (images, CSS, JS)
  • Terminate SSL (handle HTTPS)
  • Load balance between multiple Node.js processes
  • Protect your app from direct exposure to the internet

4. PM2: The Process Manager

PM2 is a production process manager for Node.js applications. It keeps your app running forever, restarts it if it crashes, and can handle zero-downtime reloads. PM2 also offers monitoring and simple log management.

How Do These Components Work Together?

Here’s a simplified data flow:

  1. Client (browser or API consumer) sends a request to your server’s IP or domain.
  2. Nginx receives the request on port 80 (HTTP) or 443 (HTTPS).
  3. Nginx forwards (proxies) the request to your Node.js app running on an internal port (e.g., 3000).
  4. Node.js processes the request and sends a response back to Nginx.
  5. Nginx returns the final response to the client.
  6. PM2 ensures the Node.js process stays alive and restarts it automatically if needed.
Tip Always run your Node.js app behind Nginx in production—never expose the Node.js port (like 3000) directly to the public internet for security and flexibility.

Visualizing the Stack

[Client] <---> [Nginx (ports 80/443)] <---> [Node.js app (port 3000)]
                                              ^
                                              |
                                         [PM2 manages process]

Why This Stack?

  • Security: Nginx shields your Node.js app from direct attacks.
  • Performance: Nginx can handle thousands of concurrent connections and efficiently serve static files.
  • Reliability: PM2 keeps your app running, even if it crashes or the server restarts.
  • Scalability: You can run multiple Node.js app instances and load balance with Nginx.
  • Maintainability: Each layer has a clear responsibility.

When Would You Use Something Else?

  • For extremely high-traffic sites, you may introduce Docker/Kubernetes or advanced load balancers.
  • For static sites, you might use only Nginx or a CDN.

But for most Node.js web applications, this stack is the gold standard.

Further Reading


Preparing Your Linux Server (Ubuntu & CentOS)

Before you can deploy your Node.js application, you need a properly set up Linux server. In this section, you’ll learn how to choose a server, connect to it securely, and update it for a clean, stable deployment environment. We’ll cover both Ubuntu and CentOS, the two most common choices for Node.js server setup.

1. Choose and Provision Your Server

You can use a cloud provider (like DigitalOcean, AWS, Linode, or Google Cloud) or your own physical machine. For most beginners, a cloud VPS is easiest.

Recommended starting specs:

  • 1 CPU core
  • 1–2 GB RAM
  • 25+ GB SSD disk
  • Ubuntu 22.04 LTS or CentOS 8/Stream

2. Connect Securely via SSH

Once your server is provisioned, connect using SSH. You’ll need the server’s IP address and the username (often root or ubuntu).

On macOS/Linux:

ssh username@your_server_ip

On Windows:

  • Use PuTTY, Windows Terminal, or WSL.

Optional: Set Up SSH Keys (Highly Recommended)

  1. Generate an SSH key on your local machine:
    ssh-keygen -t ed25519 -C "your_email@example.com"
    
  2. Copy the public key to your server:
    ssh-copy-id username@your_server_ip
    
Warning Never use simple passwords for SSH. Always use strong passwords or SSH key authentication to prevent unauthorized access.

3. Update Your Server’s Packages

Keeping your system up-to-date ensures you have the latest security patches and features.

For Ubuntu:

sudo apt update
sudo apt upgrade -y

For CentOS:

sudo dnf update -y   # (CentOS 8/Stream)

4. Install Basic Utilities (Optional but Helpful)

# For both Ubuntu and CentOS
sudo apt install curl git unzip -y    # Ubuntu/Debian
sudo dnf install curl git unzip -y    # CentOS/Fedora

Quick Checklist

  • Can you SSH into your server as root or a sudo-enabled user?
  • Did you update and upgrade all system packages?
  • (Optional) Did you set up SSH key authentication?
  • Do you have basic tools like curl and git installed?

With a prepared server, you’re now ready to install Node.js and start deploying your app.

Further Reading


Installing Node.js on Linux (Ubuntu & CentOS)

With your Linux server ready, the next step is to install Node.js and npm (Node Package Manager). This process is slightly different between Ubuntu and CentOS, and there are several methods to choose from. Here, you’ll learn the most reliable, production-friendly options—including how to use nvm (Node Version Manager) for flexibility.

1. Why Use Official Sources or nvm?

  • Official repositories often have outdated Node.js versions. The NodeSource repository or nvm offers the latest releases and better control.
  • nvm is perfect if you want to manage multiple Node.js versions (for different projects) or easily upgrade/downgrade.

2. Installing Node.js on Ubuntu (with NodeSource)

  1. Add the NodeSource APT repository (replace 18.x with your needed version):
    curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
    
  2. Install Node.js and npm:
    sudo apt install -y nodejs
    
  3. Verify installation:
    node -v
    npm -v
    

3. Installing Node.js on CentOS (with NodeSource)

  1. Add the NodeSource Yum repository:
    curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -
    
  2. Install Node.js and npm:
    sudo dnf install -y nodejs
    
  3. Verify installation:
    node -v
    npm -v
    

4. (Recommended) Managing Node.js Versions with nvm

If you want to switch Node.js versions easily (helpful for different projects), install nvm:

  1. Install nvm:
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
    # Restart your shell or source your profile:
    source ~/.bashrc  # or ~/.zshrc
    
  2. Install a Node.js version (e.g., latest LTS):
    nvm install --lts
    nvm use --lts
    
  3. Set the default version:
    nvm alias default lts/*
    
  4. Verify installation:
    node -v
    npm -v
    

When to Use nvm vs. System Packages

  • Use nvm if you need to manage multiple Node.js versions or want an unprivileged (non-root) installation.
  • Use system packages for simplicity or if you prefer managing everything as root.
Tip If you use nvm, avoid installing global npm packages as root. Always use your user account for app management to prevent permission problems.

5. Troubleshooting Common Installation Issues

  • Command not found? Make sure your shell profile (~/.bashrc, ~/.zshrc) is sourced after installing nvm.
  • Permission errors? Run commands as your user (not root) when using nvm.
  • Wrong Node.js version? Check with node -v; use nvm use to switch.

Quick Checklist

  • Did node -v and npm -v show the expected versions?
  • Did you install with nvm if you want version flexibility?
  • Can you run a simple test script?

Test Node.js installation:

node -e "console.log('Node.js is working!')"

Further Reading


Uploading and Setting Up Your Node.js Application

With Node.js installed on your server, you’re ready to deploy your application code. In this section, you’ll learn how to securely transfer files, configure your app’s environment variables, and set permissions for a production-ready deployment. These steps are crucial for a smooth "nodejs production deployment."

1. Transfer Your Application Files

There are several ways to upload your app to the server:

Option A: Using scp (Secure Copy)

From your local machine:

scp -r ./my-node-app username@your_server_ip:/home/username/
  • -r copies directories recursively.
  • Place your app in /home/username/ or a dedicated /var/www/my-node-app/ directory.

Option B: Using SFTP (GUI Tools)

  • FileZilla or WinSCP allow drag-and-drop file uploads over SFTP.
  • Connect with the same username and server IP.

Option C: Clone from Git

If your app is in a Git repository:

git clone https://github.com/yourusername/your-node-app.git

2. Organize Your Application Directory

  • Place each app in its own folder (e.g., /home/username/my-node-app/).
  • Keep node_modules out of your version control; install dependencies on the server.
  • Structure example:
    /home/username/my-node-app/
      ├── app.js
      ├── package.json
      ├── .env
      └── ...
    

3. Install Dependencies

SSH into your server, switch to the app directory, then:

cd ~/my-node-app
npm install --production
  • The --production flag skips dev dependencies, reducing attack surface.

4. Set Environment Variables for Production

Never hardcode sensitive config (like database passwords) in your code. Use environment variables, typically via a .env file:

Example .env file:

NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://user:pass@localhost:5432/dbname
SECRET_KEY=your_secret_key

Load .env in your app: (with dotenv)

require('dotenv').config();
Fact The Twelve-Factor App methodology recommends keeping configuration in the environment, not in code—this enables safer, more portable deployments.

5. Set File & Directory Permissions

  • Ensure your app is owned by your user (not root):
    sudo chown -R $USER:$USER ~/my-node-app
    
  • Permissions: directories 755, files 644 are usually safe defaults.
  • For security, never store sensitive files (like private keys) in world-readable locations.

Quick Checklist

  • Application files uploaded to the server
  • Dependencies installed with npm install --production
  • Environment variables configured securely
  • File permissions set correctly

Mini Project: Deploy a Test App

As a quick test, try uploading a simple Express.js “Hello World” app, install dependencies, and run it with node app.js to verify everything works before moving on.

Further Reading


In the next part, you’ll learn how to install and use PM2 to run your Node.js app as a background service, and configure Nginx as a secure and scalable reverse proxy. You’re well on your way to mastering beginner Node.js deployment!


Installing and Configuring PM2 for Node.js Process Management

In Part 1, you prepared your Linux server and ensured that your Node.js application was up and running. However, running a Node.js app with node app.js is not suitable for production. If your app crashes, it won’t restart automatically. If your server reboots, your app won’t start with it. This is where PM2 shines—a production-grade process manager for Node.js that makes your app reliable, monitorable, and easy to manage.

In this section, we'll walk through installing PM2 globally, launching your Node.js app with it, ensuring your process stays alive, and managing logs and status checks. This is a crucial step in any beginner Node.js deployment guide, and a must for anyone aiming to deploy Node.js on a Linux server with confidence.

1. Install PM2 Globally

PM2 is installed as an npm package. To manage your app system-wide, install it globally:

sudo npm install -g pm2
Tip If you’re using Node.js via nvm, make sure your user’s PATH is set correctly and that you’re installing PM2 for the right Node.js version.

Verify the installation:

pm2 --version

You should see a version number, confirming PM2 is ready.

2. Start and Monitor Your Node.js App with PM2

Switch to your app’s directory. Suppose your main file is app.js (adjust as needed):

cd /path/to/your/app
pm2 start app.js --name "my-app"
  • --name assigns a friendly label to your process (helpful if you run multiple apps).
  • PM2 will fork and daemonize your app, detaching it from your shell.

Check running processes:

pm2 list

You’ll see a table showing your app’s status, uptime, and memory usage.

Monitor logs in real time:

pm2 logs

To view logs for a specific app:

pm2 logs my-app

3. Configure Automatic Restarts (on Crash or Reboot)

PM2 automatically restarts your app if it crashes or is killed. To also restart your app when the server reboots, set up PM2’s startup script:

a. Generate a Startup Script

pm2 startup
  • This will print a command—copy and run it with sudo to register PM2 with your OS’s init system (systemd, upstart, etc.).

b. Save Your Process List

After starting your app(s) with PM2:

pm2 save

This saves the current process list, so PM2 can resurrect it after a reboot.

Fact PM2 can manage multiple Node.js apps (or other scripts) at once—great for microservices or monorepos.

c. Reboot Test

  1. Reboot your server:
    sudo reboot
    
  2. Log in again and run:
    pm2 list
    
    Your app should be running automatically.

4. Access Logs and Check Process Status

  • View logs:
    • Combined output: pm2 logs
    • Error logs only: pm2 logs my-app --err
    • Output logs only: pm2 logs my-app --out
  • Restart an app:
    • pm2 restart my-app
  • Stop an app:
    • pm2 stop my-app
  • Delete an app:
    • pm2 delete my-app

Mini-Project: Deploy a Sample App with PM2

Try deploying a simple Express.js server and managing it with PM2. This is a great way to practice before deploying your main application.

Common PM2 Tasks Cheat Sheet

  • List processes: pm2 list
  • Show detailed info: pm2 show my-app
  • Reload app (zero-downtime): pm2 reload my-app
  • Save process list: pm2 save
  • Kill all processes: pm2 kill
Warning Don’t forget to run `pm2 save` after adding or removing apps, or your changes won’t survive a reboot.

Further Reading


Installing and Configuring Nginx as a Reverse Proxy for Node.js

With your Node.js application running reliably under PM2, it’s time to expose it to the world. For security, scalability, and performance, it’s best practice to deploy Node.js on a Linux server behind a robust web server like Nginx, using it as a reverse proxy. This lets Nginx handle incoming HTTP/HTTPS traffic and forward requests to your Node.js process.

This section covers installation of Nginx on Ubuntu or CentOS, writing a basic reverse proxy configuration, testing the setup, and troubleshooting common issues. This is a cornerstone of any professional Node.js server setup.

1. Install Nginx on Your Server

Ubuntu (20.04/22.04+)

sudo apt update
sudo apt install nginx

CentOS (7/8/Stream)

sudo yum install epel-release
sudo yum install nginx
sudo systemctl enable nginx

Start and enable Nginx:

sudo systemctl start nginx
sudo systemctl enable nginx

Check status:

sudo systemctl status nginx

Open your server’s IP in a web browser—you should see the default Nginx welcome page.

2. Write a Basic Reverse Proxy Configuration

By default, Node.js apps run on ports like 3000, 4000, etc., not port 80/443. Nginx listens on 80/443 and proxies traffic to your app.

a. Find Your Node.js App’s Internal Port

Suppose your app listens on port 3000. Adjust as needed for your setup.

b. Create a New Nginx Server Block (Ubuntu: /etc/nginx/sites-available/yourapp)

server {
    listen 80;
    server_name your_domain.com www.your_domain.com;
location / {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

}

  • Replace your_domain.com with your domain or server IP.
  • Adjust port 3000 as needed.

Enable the config (Ubuntu/Debian):

sudo ln -s /etc/nginx/sites-available/yourapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

CentOS: Edit /etc/nginx/nginx.conf or add to /etc/nginx/conf.d/yourapp.conf with similar content, then reload Nginx.

c. Firewall Settings

Allow HTTP/HTTPS:

  • Ubuntu (UFW):
    sudo ufw allow 'Nginx Full'
    sudo ufw reload
    
  • CentOS (firewalld):
    sudo firewall-cmd --permanent --add-service=http
    sudo firewall-cmd --permanent --add-service=https
    sudo firewall-cmd --reload
    

3. Test Nginx and Node.js Integration

  • Visit http://your_domain.com or your server’s IP in a browser.
  • You should see your Node.js app’s output (not Nginx’s welcome page).
  • If your app has a /health endpoint, test it: http://your_domain.com/health.

Check for errors:

sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/access.log

Common issues:

  • 502 Bad Gateway = Node.js app not running or wrong port
  • 404 Not Found = Wrong location block or file path

4. Troubleshooting Nginx-Node.js Setups

  • Port conflicts: Ensure your Node.js app is not set to listen on 80/443 if Nginx is using those ports.
  • Permissions: Make sure Nginx can connect to localhost:3000 (default SELinux/AppArmor settings can block this; consult OS docs).
  • Reload after changes: Always run sudo nginx -t (to test configs) followed by sudo systemctl reload nginx.
Tip Use `pm2 logs` and `sudo tail -f /var/log/nginx/error.log` in split terminals to debug issues in real-time.

Mini-Checklist: Basic Nginx Reverse Proxy for Node.js

  • Nginx installed and running
  • Node.js app running under PM2
  • Nginx server block points to correct port
  • Firewall allows HTTP/HTTPS
  • Browser shows your Node.js app via Nginx

Further Reading


Securing Your Node.js Deployment: Basic Best Practices

Now that your Node.js app is accessible through Nginx, it’s essential to lock things down. Security is a core part of any "nodejs production deployment". Even a simple misconfiguration can expose your server to attacks. This section covers basic security: firewall setup, enabling HTTPS, and hardening your Nginx and Node.js configuration, with practical steps for both Ubuntu and CentOS.

1. Configure a Firewall (UFW or firewalld)

A firewall restricts unwanted network traffic, protecting your server.

Ubuntu (UFW)

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
  • OpenSSH keeps your SSH access open so you don’t lock yourself out.
  • 'Nginx Full' opens HTTP and HTTPS ports (80, 443).

CentOS (firewalld)

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
Fact Most cloud providers (AWS, DigitalOcean, etc.) offer their own firewall or "security group" settings. Always secure both the server and the cloud firewall.

2. Set Up HTTPS with Let’s Encrypt (Free SSL)

Serving your app over HTTPS is critical. Let’s Encrypt makes it free and easy.

a. Install Certbot

  • Ubuntu:
    sudo apt install certbot python3-certbot-nginx
    
  • CentOS:
    sudo yum install certbot python3-certbot-nginx
    

b. Obtain and Install a Certificate

Replace your_domain.com with your domain:

sudo certbot --nginx -d your_domain.com -d www.your_domain.com
  • Certbot will auto-configure Nginx for HTTPS, including redirection from HTTP to HTTPS.
  • Follow prompts to choose if you want HTTP-to-HTTPS redirect.

Test renewal:

sudo certbot renew --dry-run
Warning If your domain's DNS is not pointed at your server, Let’s Encrypt validation will fail. Always update DNS first.

3. Harden Nginx and Node.js Settings

  • Disable server tokens in Nginx:
    server_tokens off;
    
    Add this in http { ... } block in /etc/nginx/nginx.conf.
  • Limit request size:
    client_max_body_size 1M;
    
  • Restrict allowed HTTP methods:
    if ($request_method !~ ^(GET|POST)$ ) {
        return 444;
    }
    
  • Run Node.js as a non-root user. Never run your app as root.
  • Update your dependencies and Node.js version regularly.

4. Linux Permissions and User Roles Basics

  • Files and folders: Set correct permissions. For most apps, 755 for folders and 644 for files is sufficient.
  • PM2: Run pm2 as your normal user, not as root. If you use sudo pm2, your apps might not start on reboot for your regular user.
  • Limit sudo access: Only give sudo privileges to trusted users.

Mini-Project: Test Your Security

  • Try accessing your server on ports other than 80/443/22—they should be blocked.
  • Test HTTPS by visiting https://your_domain.com and ensuring you get a secure lock icon in your browser.

Further Reading


Testing and Verifying Your Production Node.js Deployment

You’ve now completed your basic Linux server Node.js deployment: your app is managed by PM2, traffic is routed through Nginx, and security basics are in place. Before you celebrate, it’s crucial to rigorously test your deployment. This section guides you through browser-based checks, log analysis, and simple load simulation to ensure your app is stable and correctly served.

1. Test Your Node.js App Through Nginx from a Browser

  • Open a browser and enter your domain name (e.g., https://your_domain.com).
  • Confirm your app loads and works as expected.
  • If you have a health check route (e.g. /health), visit it to ensure backend connectivity.
  • Try both http:// and https:// versions—ensure HTTP redirects to HTTPS.

2. Check Logs for Node.js, PM2, and Nginx

Log files are your first stop for troubleshooting.

  • PM2 logs:
    pm2 logs my-app
    
  • Nginx logs:
    sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log
    
  • Application logs: If your app writes its own logs, review them as well.
Tip Use log search tools like `grep` to quickly find errors or unusual events in large log files.

3. Simulate Traffic to Verify Stability

You can use tools like ab (Apache Benchmark) or curl to simulate traffic:

ab -n 100 -c 10 https://your_domain.com/
  • -n 100 sends 100 requests; -c 10 means 10 concurrent requests.
  • Monitor your server’s CPU/memory with top or htop while testing.
  • Watch PM2 and Nginx logs for errors or performance bottlenecks.

4. Identify and Resolve Common Deployment Errors

  • 502 Bad Gateway:
    • Node.js app not running or wrong port in Nginx config
  • 403 Forbidden:
    • File/folder permissions issue; check Nginx and app directory permissions
  • SSL/TLS errors:
    • Certificate missing or expired; re-run Certbot
  • PM2 not starting apps after reboot:
    • Forgot pm2 save or didn’t run pm2 startup as instructed

Mini-Checklist: Production Deployment Works!

  • App loads via domain name (and HTTPS)
  • HTTP redirects to HTTPS
  • No critical errors in Nginx, PM2, or app logs
  • Server stands up to basic load testing

What’s Next?

In future parts, we’ll cover advanced topics such as scaling Node.js apps, rolling deployments, environment variables, and production monitoring.

Further Reading


Automating Node.js Deployment with PM2 Ecosystem File

In previous parts, you learned to run your Node.js app on a Linux server with PM2 and Nginx. Now, let's elevate your deployment workflow by introducing the PM2 ecosystem file. This powerful feature lets you define multiple apps, set environment-specific variables, and automate process management — all in a single, version-controlled config file.

The ecosystem file is essential for scalable, repeatable deployments. It also makes managing complex production environments much simpler, especially as your projects grow or when you need to deploy multiple Node.js applications on the same server.

1. Create a PM2 Ecosystem File

PM2 supports configuration files in either JSON or JS (JavaScript) format. The JS format (ecosystem.config.js) is more flexible and widely used.

To generate a sample ecosystem file:

pm install pm2 -g   # if you haven't already
yarn global add pm2 # or use yarn
pm2 init            # creates ecosystem.config.js

This creates a basic ecosystem.config.js in your project directory.

2. Customize Your ecosystem.config.js

Open the file in your favorite editor. Here’s a simple example for a single app:

module.exports = {
  apps: [
    {
      name: 'my-app',
      script: './app.js',
      instances: 1,
      env: {
        NODE_ENV: 'development',
        PORT: 3000
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 8080
      }
    }
  ]
};

What you can configure:

  • name: Name of your process as it appears in PM2.
  • script: Entry point file (e.g. app.js).
  • instances: Use "max" or a number — max auto-scales to CPU cores.
  • env: Default environment variables.
  • env_production: Vars for production. PM2 loads these with the --env production flag.
Tip You can add comments and logic to `ecosystem.config.js` since it's just a JS module.

3. Managing Multiple Node.js Apps

To deploy more than one app on your Linux server, simply add more entries in the apps array:

module.exports = {
  apps: [
    {
      name: 'api-server',
      script: './api.js',
      env_production: { NODE_ENV: 'production', PORT: 4000 }
    },
    {
      name: 'web-client',
      script: './client.js',
      env_production: { NODE_ENV: 'production', PORT: 5000 }
    }
  ]
};

This is ideal for microservices or hosting multiple projects on one server.

4. Running and Managing Apps with PM2

To start all apps defined in your ecosystem file:

pm2 start ecosystem.config.js --env production

Common PM2 commands:

  • pm2 status — View all processes.
  • pm2 restart <name> — Restart a specific app.
  • pm2 reload all — Graceful reload of all apps (zero-downtime for HTTP servers).
  • pm2 stop <name> and pm2 delete <name> — Stop or remove an app.

5. Automate Restarts and Updates

PM2 will automatically restart your app if it crashes. You can also set up other behaviors:

module.exports = {
  apps: [
    {
      name: 'my-app',
      script: './app.js',
      watch: false, // Set to true for auto-restart on file changes (not recommended in production)
      max_restarts: 5, // Limit restarts
      restart_delay: 1000 // Delay between restarts (ms)
    }
  ]
};
  • watch: Auto-restart on file changes (great for dev, risky in prod).
  • max_restarts: Prevents endless crash loops.
  • restart_delay: Adds a pause between restarts.
Warning Avoid using `watch: true` in production. Unintended file changes or permission issues can trigger endless restart cycles.

6. Environment Variables for Production

Store sensitive settings like database URLs or API keys in the environment variable blocks. This keeps your codebase clean and configuration flexible.

Example:

env_production: {
  NODE_ENV: 'production',
  PORT: 8080,
  DATABASE_URL: 'postgres://user:pass@localhost/dbname'
}
Fact PM2 merges `env_production` variables with `env` when you run with `--env production`.

7. Micro-Project: Version Control Your Ecosystem File

  1. Add ecosystem.config.js to your project root.
  2. Commit it to your Git repository (omit secrets, or use placeholders).
  3. Share with your team for consistent deployments.

8. Recap

Using an ecosystem file with PM2 is a best practice for any serious Node.js production deployment on Linux. It enables:

  • Repeatable, automated deployments
  • Easy management of multiple apps
  • Environment-specific configs
  • Hassle-free restarts and scaling

Further Reading


Best Practices for Node.js Production Deployment on Linux

Now that you have a robust deployment with PM2 and Nginx, it's crucial to follow industry best practices for performance, security, and maintainability. Whether your Node.js app is public-facing or internal, applying these practices will help you avoid downtime, data loss, and security issues.

1. Security Best Practices

1. Keep Node.js and Dependencies Updated

  • Regularly update Node.js, Nginx, PM2, and all app dependencies.
  • Use tools like npm audit or yarn audit to check for vulnerabilities.
  • Subscribe to security mailing lists for your Linux distribution.

2. Secure Environment Variables

  • Never commit secrets (API keys, DB passwords) to version control.
  • Use .env files (with dotenv) or PM2’s environment blocks.
  • Restrict file permissions (chmod 600 .env).

3. Harden Nginx and Linux

  • Use HTTPS (see Let's Encrypt or a commercial CA).
  • Set strong HTTP security headers in your Nginx config:
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
  • Enable a firewall (e.g., UFW on Ubuntu) to allow only necessary ports (80, 443).
Tip After changing firewall settings, always test connectivity from another device to avoid locking yourself out.

4. Drop Privileges

  • Run Node.js apps as a non-root user.
  • Only allow root for Nginx if absolutely necessary (e.g., to bind to ports <1024), then drop privileges with user directive.

2. Performance Tuning

1. Use PM2 Clustering

  • Leverage multi-core CPUs by running your app in cluster mode:
pm2 start ecosystem.config.js -i max --env production
  • PM2 will fork as many processes as you have CPU cores, balancing requests.

2. Enable Gzip Compression in Nginx

gzip on;
gzip_types text/plain application/json application/javascript text/css;

3. Cache Static Assets

  • Set long Cache-Control headers for static files in your Nginx config.

3. Monitoring and Logging

1. Monitor Node.js and Nginx

  • Use PM2’s dashboard: pm2 monit for real-time stats.
  • Enable PM2 log rotation:
pm2 install pm2-logrotate
  • Monitor Nginx logs: /var/log/nginx/access.log and /var/log/nginx/error.log.

2. Consider External Monitoring Tools

  • Services like Keymetrics, New Relic, or Datadog offer advanced metrics, uptime checks, and alerts.

4. Backups and Disaster Recovery

1. Automate Backups

  • Regularly back up your database and any important files.
  • Use cron jobs or backup services.

2. Test Restores

  • Periodically validate that your backups can be restored successfully.

3. Document Your Recovery Steps

  • Maintain a runbook with step-by-step instructions in case of disaster.

5. Ongoing Maintenance

  • Schedule regular software updates (OS, Node.js, Nginx, PM2).
  • Rotate logs and monitor disk usage.
  • Review access logs for suspicious activity.
  • Remove unused packages and close unnecessary ports.
Fact PM2 can be configured to send you email or Slack notifications on process failure via modules like `pm2-slack`.

Micro-Checklist: Node.js Production Deployment on Linux

  • Node.js app runs under a non-root user
  • PM2 ecosystem file in version control
  • Nginx reverse proxy with HTTPS and security headers
  • Firewall enabled, only HTTP/HTTPS allowed
  • Monitoring and log rotation configured
  • Regular backups and disaster recovery plan

Further Reading


Troubleshooting Common Node.js Deployment Issues

Even with a solid deployment strategy, issues can arise when deploying Node.js apps with Nginx and PM2 on a Linux server. This section covers the most frequent problems and provides practical steps for diagnosing and resolving them.

1. Diagnosing Nginx and Node.js Integration Issues

Symptom: You visit your domain, but see a 502 Bad Gateway or 504 Gateway Timeout.

Steps to troubleshoot:

  1. Check PM2 App Status

    • Run pm2 status to ensure your app is online.
    • If it's not, check logs with pm2 logs <app-name>.
  2. Verify App is Listening on the Expected Port

    • In your ecosystem.config.js, confirm the PORT matches your Nginx proxy config.
    • Use netstat -tlnp or ss -tlnp to see which ports Node.js is binding to.
  3. Inspect Nginx Error Logs

    • Look at /var/log/nginx/error.log for clues.
    • Common errors: "Connection refused", "upstream timed out", or permission denied.
  4. Test Direct Connection

    • Use curl http://localhost:PORT to check if your Node.js app responds directly.

2. Resolving PM2 Process and Log Errors

Symptom: PM2 apps crash, restart endlessly, or logs aren't updating.

Steps to troubleshoot:

  1. Read PM2 Logs

    • pm2 logs <app-name> for real-time output.
    • Check for syntax errors, uncaught exceptions, or missing environment variables.
  2. Check File Permissions

    • Ensure PM2 and your app have read/write access to log files and any directories used.
  3. Address Memory Leaks or High CPU

    • Use pm2 monit to watch resource usage.
    • Consider limiting memory with max_memory_restart in your ecosystem file:
max_memory_restart: '300M'
Warning Setting too low a `max_memory_restart` value can cause unnecessary restarts. Monitor your app's baseline usage first.

3. Addressing Firewall and Port Conflicts

Symptom: App runs, but cannot be reached from the outside, or Nginx can't reach Node.js.

Steps to troubleshoot:

  1. Check UFW/Firewall Rules

    • sudo ufw status (Ubuntu) or sudo firewall-cmd --list-all (CentOS).
    • Ensure required ports (e.g., 80, 443 for Nginx; your Node.js app port if accessed directly) are open.
  2. Find Port Conflicts

    • Use netstat -tlnp or ss -tlnp to find if another process is using your desired port.
    • Change your app's port if needed and update Nginx config accordingly.

4. Fixing Permission and Ownership Problems

Symptom: PM2 or Nginx fails to start, or there's a "permission denied" error in logs.

Steps to troubleshoot:

  1. Check File Ownership

    • App files and logs should be owned by the user running PM2 (avoid root).
    • Use chown to reset ownership:
      sudo chown -R deployuser:deployuser /path/to/app
      
  2. Set Correct File Permissions

    • App code: chmod 644, scripts: chmod 755 if executable.
    • Sensitive files (like .env): chmod 600.
  3. Restart Services After Permission Changes

    • Reload PM2: pm2 restart all
    • Reload Nginx: sudo systemctl reload nginx

Case Checklist: General Troubleshooting

  • PM2 app running and healthy (pm2 status)
  • App responds directly on its port
  • Nginx logs show no critical errors
  • Port and firewall rules allow desired traffic
  • Correct file and directory permissions
Tip If all else fails, try a full server reboot to clear up lingering process or port lock issues.

Further Reading


Case Study: Deploying Multiple Node.js Apps on One Server

It's common to host more than one Node.js application on a single Linux server, whether for microservices, internal tools, or separate client projects. Let's walk through a realistic scenario: deploying two apps, api-server and web-client, each with its own domain, using PM2 and Nginx.

1. Prepare Your Project Structure

Assume the following directory layout:

/var/www/
  ├── api-server/
  │     └── app.js
  └── web-client/
        └── app.js

Each app should have its own Node.js project, ideally with separate package.json files.

2. Create a Unified PM2 Ecosystem File

You can place a single ecosystem.config.js at /var/www/ to control both apps:

module.exports = {
  apps: [
    {
      name: 'api-server',
      script: '/var/www/api-server/app.js',
      env_production: { NODE_ENV: 'production', PORT: 4000 }
    },
    {
      name: 'web-client',
      script: '/var/www/web-client/app.js',
      env_production: { NODE_ENV: 'production', PORT: 5000 }
    }
  ]
};

Start both apps:

pm2 start ecosystem.config.js --env production
Fact PM2 can monitor, restart, and log multiple Node.js apps simultaneously, simplifying multi-app deployments.

3. Configure Nginx for Multiple Domains

Suppose you want api.example.com to point to api-server, and www.example.com to web-client.

Create two Nginx server blocks:

# /etc/nginx/sites-available/api.example.com
server {
    listen 80;
    server_name api.example.com;
    location / {
        proxy_pass http://127.0.0.1:4000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

/etc/nginx/sites-available/www.example.com

server { listen 80; server_name www.example.com; location / { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }

Enable both configs:

sudo ln -s /etc/nginx/sites-available/api.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/www.example.com /etc/nginx/sites-enabled/
sudo systemctl reload nginx

4. Set Up Domains and DNS

  • Point api.example.com and www.example.com to your server’s public IP in your DNS provider's dashboard.
  • Allow propagation time (may take minutes to hours).

5. Monitor and Control Multiple Apps with PM2

  • pm2 status shows both apps.
  • Restart one app without affecting the other:
pm2 restart api-server
  • View logs for each app:
pm2 logs web-client

6. Avoiding Common Pitfalls

  • Port Conflicts: Ensure each Node.js app listens on a unique port.
  • Domain Misrouting: Double-check Nginx server_name and proxy_pass settings.
  • Resource Contention: Monitor server CPU and memory. Consider upgrading or splitting to multiple servers if needed.
  • Permission Issues: Each app should run under the correct user, with isolated directories.
Warning Never use the same port for more than one Node.js app on the same server — this will cause unpredictable behavior.

Mini-Project: Add a Third App

Try deploying a third Node.js app (admin-dashboard) on port 6000, with its own domain (admin.example.com). Update your PM2 ecosystem file and add a new Nginx server block. Test that all three apps are reachable by their domains!

Further Reading


Conclusion and Next Steps

You’ve now completed a full, beginner-friendly guide to deploying Node.js on a Linux server with Nginx and PM2. By following these steps, you’ve set up a production-ready environment, automated your deployments, and learned how to troubleshoot and manage multiple applications with confidence.

Let’s quickly recap what you’ve accomplished:

  • Installed Node.js and PM2 on your Linux server.
  • Configured Nginx as a reverse proxy to handle incoming traffic and serve your Node.js apps securely.
  • Automated deployments using PM2’s powerful ecosystem file.
  • Applied best practices for security, monitoring, and reliability.
  • Troubleshot common deployment issues and learned how to scale to multiple apps on a single server.

Where to Go Next

  • Add SSL/TLS (HTTPS): Secure your apps with free certificates from Let’s Encrypt.
  • Automate deployments: Explore CI/CD tools (GitHub Actions, Jenkins, etc.) to push code automatically.
  • Advanced monitoring: Integrate external monitoring for uptime, analytics, and alerts.
  • Scaling out: Consider Docker, Kubernetes, or cloud platforms for larger projects with high availability.
  • Performance tuning: Dive deeper into Node.js profiling, load testing, and caching strategies.
Tip Keep learning! The Node.js ecosystem evolves rapidly. Stay up to date with the latest deployment techniques and security best practices.

Further Reading


With these foundations, you’re ready to launch and maintain robust Node.js apps on Linux. Continue to refine your deployment workflows, monitor your systems, and explore advanced topics as your needs grow. Good luck!

About Prateeksha Web Design

Prateeksha Web Design helps businesses turn tutorials like "Deploy Node.js on a Linux Server with Nginx and PM2 (Beginner Deployment Guide)" into real-world results with custom websites, performance optimization, and automation. From strategy to implementation, our team supports you at every stage of your digital journey.

Chat with us now Contact us today.

Sumeet Shroff
Sumeet Shroff
Sumeet Shroff is a renowned expert in web design and development, sharing insights on modern web technologies, design trends, and digital marketing.

Comments

Leave a Comment

Loading comments...