node server.js by itself cannot do.
Why AI Coders Need to Know This
There's a specific moment every vibe coder hits when they move beyond Vercel and Netlify. You've got a VPS — a real server you rented from DigitalOcean or Hetzner. You SSH'd in, followed the instructions, ran your app, opened the URL in your browser, and it worked. You felt like a wizard.
Then you closed your laptop.
The next morning you open your phone to show a friend, and the site is down. Not broken — just completely unreachable. You SSH back in, run node server.js again, and it comes back. What happened?
Your app was running as a foreground process attached to your terminal session. Think of it like a generator you're holding at a job site. The moment you put it down and walk away, it stops. PM2 is the permanent power hookup. It runs your app as a background service, independent of any terminal session, so it keeps running whether you're connected or not.
Every serious vibe coder who deploys to a VPS needs PM2 — or something equivalent. AI knows this. When you ask Claude or ChatGPT to help you deploy a Node.js app to a VPS, it will almost always suggest PM2. The problem is that AI sometimes misses the critical second and third steps that make PM2 actually work long-term. This article covers all three steps so you don't have to figure it out the hard way.
Here's what you need to know:
- What PM2 does — and why
node server.jsisn't enough for a real server - The three commands that actually matter — start, startup, and save
- How to read PM2's output — so you know when something is wrong
Real Scenario: Your Site Died When You Closed Your Laptop
"I deployed my Node.js app to a DigitalOcean Droplet. I SSH'd in, ran 'node server.js', and the site worked at my server's IP address. But then I closed my laptop, and the next day the site was completely down. When I SSH'd back in and ran node server.js again it came back. How do I make it stay running permanently?"
This is one of the most common first-time VPS questions. The answer is PM2, and Claude knows it. It will typically respond with a set of commands to install PM2, start your app with it, and set up automatic startup on reboot.
But here's the catch: AI often gives you the first command correctly and then undersells the second and third ones. Those two — pm2 startup and pm2 save — are what determine whether your app survives a server reboot. Without them, your app runs fine until the next time your server restarts (for a system update, a power hiccup, or any other reason), and then it's down again.
Let's look at everything AI generates and what each part actually does.
What AI Generated
Here's what a well-formed AI response looks like when you ask it to set up PM2 on a VPS. This is the full picture — installation, starting your app, checking status, viewing logs, and the two critical reboot-persistence commands.
Step 1 — Install PM2 globally
npm install -g pm2
The -g flag installs PM2 globally on your server — meaning it's available as a command anywhere, not just inside a specific project folder. You only do this once per server.
Step 2 — Start your app with PM2
pm2 start server.js --name my-app
This tells PM2 to run server.js and give it the name my-app. The name is how you'll refer to this app in future PM2 commands. Your app is now running in the background. You can close your terminal, and it will stay running.
Step 3 — Check that it's actually running
pm2 status
This shows a table of every app PM2 is managing. You should see your app listed with a status of online.
Step 4 — Set up automatic startup on reboot (critical)
pm2 startup
This command outputs another command that you need to copy and run. It looks something like this (the exact path varies by your server's Linux distribution):
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u your-username --hp /home/your-username
Copy that output exactly and run it. This registers PM2 as a system service so it starts automatically when your server boots.
Step 5 — Save your current app list (also critical)
pm2 save
This saves the list of apps currently running in PM2. When the server reboots and PM2 starts automatically, it reads this saved list and restarts all your apps. Without this step, PM2 starts on reboot but doesn't know which apps to run.
The ecosystem.config.js approach
For anything more than a single simple app, AI will often generate an ecosystem.config.js file. This is a configuration blueprint that tells PM2 everything it needs in one place:
// ecosystem.config.js — put this in your project root
module.exports = {
apps: [
{
name: 'my-app',
script: 'server.js',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
PORT: 3000
}
}
]
};
Once this file exists, you start your app with:
pm2 start ecosystem.config.js
Then still run pm2 startup and pm2 save as before. The ecosystem file is just a better way to define the app — the startup/save steps are always required regardless.
Understanding Each Part
Let's break down what's actually happening here — not the computer science, just what it means for keeping your app alive.
The Problem: Foreground vs. Background Processes
When you SSH into a server and type node server.js, you're running your app as a foreground process. It's the same as running a power tool with an extension cord that runs back to your truck. The tool works, but the moment you drive away, the cord pulls out and the tool stops.
Your terminal session is that extension cord. When the session ends — you close your laptop, your internet drops, you type exit — the operating system sends a signal to all processes attached to that session: "shut down." Your Node.js app obeys and dies.
PM2 runs your app as a background service, completely detached from any terminal. It's the permanent power hookup wired directly into the building's electrical system. You can leave the job site, come back a week later, and the power is still on.
Auto-Restart: The Crash Safety Net
Even with PM2 keeping your app running, code can crash. An unhandled error, a bad API response, a memory leak — any of these can bring down a Node.js process. Without PM2, the app is simply dead until someone manually restarts it.
PM2 watches your app. The moment it detects the process has died, it starts it again automatically. This usually happens in less than a second. Most users never notice.
The autorestart: true line in ecosystem.config.js controls this behavior. It's enabled by default — you'd have to actively turn it off to disable it. Think of it like a GFCI outlet that trips and resets itself instead of requiring someone to physically push the reset button.
The startup + save Combination
This is the most misunderstood part of PM2, and the one AI most commonly explains poorly.
pm2 startup makes PM2 itself start on server boot. It registers PM2 with the operating system's service manager (usually systemd on modern Linux servers). After running this, PM2 will launch when the server boots — but it won't know which apps to run.
pm2 save takes a snapshot of which apps are currently running and saves it to disk. When PM2 starts on the next boot, it reads this snapshot and restarts exactly those apps.
You need both. pm2 startup without pm2 save is like hiring a building manager but not giving them the keys. They show up for work but can't open anything.
Important: if you add or remove apps, remember to run pm2 save again to update the snapshot. PM2 won't automatically track new apps for reboot persistence — you have to explicitly save.
The ecosystem.config.js File
Think of this file as the blueprint for your app's deployment. Instead of remembering which command-line flags you used, everything lives in one file you can version-control with git.
The key fields to know:
name— the identifier PM2 uses for this app in all status and log commandsscript— the file that starts your app (could beserver.js,dist/index.js, or even an npm script)instances— how many copies of your app to run (almost always 1 unless you know you need more)autorestart— whether to restart after a crash (default true, leave it)watch— whether to restart on file changes (set tofalsein production — you don't want your app restarting every time you edit a file)max_memory_restart— restart if memory usage exceeds this limit (a simple safeguard against memory leaks)env— environment variables to inject when PM2 starts the app
If you're deploying a Next.js app, an Express API, or any other Node.js server, the ecosystem file gives AI a clean template to fill out. You can commit it to your repo and use it to redeploy consistently on any server.
What AI Gets Wrong About PM2
AI is generally solid at PM2 basics. But it makes a handful of consistent mistakes that can leave your app in a worse state than if you'd never used PM2 at all. Here are the ones to watch for.
1. Skipping pm2 startup and pm2 save
This is the most common and most damaging mistake. AI tells you to run pm2 start, your app starts, everything looks great. Then your server reboots for a routine update and your app is dead again — because PM2 never registered with the system's boot process.
The fix is always the same two commands, run in order:
pm2 startup
# copy and run the command it outputs, then:
pm2 save
If you did this correctly, you can test it by rebooting your server (sudo reboot) and checking after it comes back up. Your app should be running without you doing anything. If it's not, something in the startup step didn't work.
Every time AI helps you with PM2 and doesn't mention these two steps, prompt it explicitly: "Does this include setting up PM2 to start on server reboot?"
2. Suggesting PM2 for Serverless or Platform Deployments
PM2 is for VPS deployments — servers you manage yourself. It has no place in deployments to Vercel, Netlify, Railway, Fly.io, or any other platform-as-a-service. Those platforms handle process management for you. Adding PM2 to a Vercel project would be like hiring a building manager for a hotel room — the hotel already handles that.
If AI suggests PM2 and you're not on a VPS, ask it to clarify: "Is this project deployed to a VPS or a managed platform?" The answer determines whether PM2 is relevant at all.
3. Recommending Cluster Mode When You Don't Need It
PM2 has a cluster mode that runs multiple copies of your app to use all CPU cores. It's a real feature for high-traffic production apps. But AI sometimes suggests it as a default, which creates unnecessary complexity for apps that don't need it.
Cluster mode looks like this in an ecosystem file:
// ⚠️ Don't use this unless you know you need it
{
instances: 'max', // or a specific number like 4
exec_mode: 'cluster'
}
The problem: cluster mode requires your app to be stateless (no in-memory sessions, no local file writes for shared state). If your app wasn't designed for this, cluster mode can cause subtle, hard-to-debug bugs where one instance doesn't see the state set by another instance.
Unless you're already dealing with a CPU bottleneck on a high-traffic server, use instances: 1 and skip exec_mode: 'cluster' entirely.
4. Wrong Script Path in ecosystem.config.js
If your app has a build step — like a TypeScript project that compiles to a dist folder, or a Next.js app — AI sometimes points PM2 at the source file instead of the compiled output:
// ❌ Wrong — this is the TypeScript source, not the compiled output
script: 'src/server.ts'
// ✅ Correct — PM2 should run the compiled JavaScript
script: 'dist/server.js'
If PM2 starts your app but it immediately errors or crashes (you'll see errored status in pm2 status), check the script path first. Run pm2 logs your-app-name to see the exact error message.
How to Debug With AI
When PM2 isn't working the way you expect, the output from two commands tells you almost everything you need to know. Here's how to read them and what to feed back to your AI.
pm2 status — Your Dashboard
pm2 status
This shows a table like this:
┌────┬────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │
├────┼────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┤
│ 0 │ my-app │ default │ 1.0.0 │ fork │ 12345 │ 2D │ 0 │ online │
└────┴────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┘
What to read:
- status — the most important column.
onlinemeans running.stoppedmeans it was manually stopped.erroredmeans it crashed and PM2 gave up trying to restart it. - ↺ (restart count) — the number of times PM2 has restarted this app. A value of 0 is fine. A value climbing into the hundreds means your app is crash-looping — something is wrong with the code and PM2 keeps restarting it.
- uptime — how long the app has been running since its last start. "2D" means two days, which is healthy. "5s" means it just restarted — check why.
pm2 logs — Your Diagnostic Report
pm2 logs my-app
This streams your app's log output to the terminal in real time. Press Ctrl+C to stop watching. If you want to see only the last 50 lines without watching live:
pm2 logs my-app --lines 50
The log output is split into two streams:
- stdout — normal output from your app (what you'd see when running it manually)
- stderr — error output, including crash messages and stack traces
When your app is in errored status or restarting repeatedly, the crash error will be in the stderr stream. Copy it and paste it directly to your AI:
"My PM2 app is showing 'errored' status. Here's the output from 'pm2 logs my-app --lines 50': [paste the logs]. The app is a Node.js Express server. What's causing this crash and how do I fix it?"
The logs tell you everything AI needs. Stack traces, error messages, missing environment variables — they all show up in the logs. Don't try to debug PM2 problems without reading the logs first.
Common Status Situations and What to Do
Status: errored, restart count in the hundreds
Your app is crash-looping. Something in your code is failing on every start. Check pm2 logs for the error message. Common causes: missing environment variables, wrong script path, or a dependency not installed in production.
Status: online, but the site is still unreachable
PM2 is running your app, but something else is blocking it. Check that your app is listening on the right port. Check that Nginx (if you're using it as a reverse proxy) is configured correctly. Check your server's firewall settings.
Status: stopped
The app was manually stopped. Run pm2 start my-app or pm2 restart my-app to bring it back. If it keeps stopping on its own, that's a crash — check the logs.
App not listed at all after a reboot
You ran pm2 start but skipped pm2 startup and pm2 save. Your apps weren't saved before the reboot. Start your app again with pm2 start ecosystem.config.js, then run pm2 startup, copy the command it outputs, run it, then run pm2 save.
Quick Reference: PM2 Commands You'll Actually Use
| Command | What It Does | When to Use It |
|---|---|---|
npm install -g pm2 |
Installs PM2 on your server | Once per server, first time setup |
pm2 start server.js --name my-app |
Starts a single file with a name | Simple one-file apps |
pm2 start ecosystem.config.js |
Starts app(s) using a config file | Most projects (recommended) |
pm2 status |
Shows all apps and their current status | Checking if everything is running |
pm2 logs my-app |
Streams live logs for an app | Debugging crashes or unexpected behavior |
pm2 logs my-app --lines 50 |
Shows last 50 lines of logs | Getting a snapshot to paste into AI |
pm2 restart my-app |
Restarts an app without stopping PM2 | After deploying new code |
pm2 stop my-app |
Stops the app (PM2 keeps watching it) | Temporarily pausing an app |
pm2 delete my-app |
Removes the app from PM2 entirely | Decommissioning an app |
pm2 startup |
Generates the command to enable boot persistence | After first install — run once per server |
pm2 save |
Saves current app list for reboot persistence | After any change to running apps |
pm2 reload my-app |
Zero-downtime restart (for cluster mode) | Only relevant with cluster mode |
What to Learn Next
PM2 solves the "app stays running" problem. But it's one piece of a full VPS deployment. Here's what connects to it:
Frequently Asked Questions
What is PM2 and why do I need it?
PM2 is a process manager for Node.js applications. When you run a Node.js app on a VPS with a command like node server.js, the app only stays alive while your terminal session is open. The moment you close your laptop or disconnect from SSH, the process dies and your site goes down. PM2 runs your app in the background, independent of any terminal session, and automatically restarts it if it crashes. It also saves your app configuration so that your app restarts automatically if the whole server reboots.
Why does my Node.js app stop when I close the terminal?
When you SSH into a server and run node server.js, that process is attached to your terminal session. It's like a machine that only runs while you're holding the power button. The moment your SSH session ends — because you closed your laptop, your internet dropped, or you simply typed exit — the operating system kills all processes that were attached to that session, including your app. PM2 detaches your app from the terminal session entirely, so it keeps running whether you're connected or not.
What is ecosystem.config.js in PM2?
ecosystem.config.js is a configuration file that tells PM2 everything it needs to know to run your app: the name of the app, which file to start, environment variables, and more. Instead of typing a long PM2 command every time you want to start your app, you define everything once in this file and then just run pm2 start ecosystem.config.js. It's the PM2 equivalent of a blueprint — write it once, reference it forever. You can also commit it to your git repository so it travels with your code.
What does 'pm2 startup' do?
The pm2 startup command makes PM2 itself start automatically when your server reboots. Without this step, PM2 and all your apps would stop running every time the server restarts (for updates, maintenance, or an unexpected crash). After running pm2 startup, you also need to run pm2 save to save the list of currently running apps. Together, these two commands ensure your app survives a server reboot. This is the most commonly missed step when setting up PM2 for the first time.
Should I use PM2 with Vercel or Netlify?
No. PM2 is only for apps running on a VPS — a server you manage yourself. Vercel, Netlify, Railway, and Fly.io all handle process management for you automatically — they are platform-as-a-service tools where you do not manage the underlying server. If you are deploying to one of those platforms, you do not need PM2. PM2 is the tool you reach for when you have moved beyond those platforms and are managing your own server.
How do I check if my app is running with PM2?
Run pm2 status or pm2 list in your terminal. This shows a table of all apps PM2 is managing, their current status (online, stopped, errored), CPU and memory usage, and how many times they have restarted. If your app shows online in the status column, it is running. If it shows errored or keeps restarting, check the logs with pm2 logs your-app-name to see what is going wrong.
Last updated: March 21, 2026. Tested with PM2 5.x and Node.js 22 LTS on Ubuntu 24.04 LTS.