Production Incident: Node.js Application Did Not Start After Server Reboot (PM2 + systemd Fix)

Search for a command to run...

No comments yet. Be the first to comment.
The Ops Fix Hub is a dynamic series designed to tackle real-world challenges in the DevOps and CloudOps domains. "Breaking Down Barriers, One Ops Fix at a Time."
A Zero-Downtime, Step-by-Step Implementation Guide 1. Overview In this post, we walk through a real production migration of a Kubernetes workload from NGINX Ingress Controller to Kubernetes Gateway API, implemented using Envoy Gateway, on Amazon EKS...
This migration was performed on a production workload where cost reduction was prioritized over zone-level high availability.

1. Overview What I Designed I designed a hybrid infrastructure architecture: Terraform → Foundation Layer Crossplane → Dynamic Lifecycle Layer ArgoCD → GitOps Enforcement This created a continuou

Cross-cloud VM migration is not a disk copy task. It is: An access model transformation A replication lifecycle management exercise A downtime control operation A cost boundary decision We execu

When AWS introduced AWS DevOps Agent, I was less interested in feature lists and more interested in one practical question. Can it actually reduce investigation time during real production-style failu

Migrating object storage across cloud providers is not a copy task.It is a cost, network, and security boundary problem. We migrated 10+ TB of object data from Google Cloud Storage to Amazon S3 under

We were running a Node.js backend using PM2 on a Linux server.
Application details:
Process manager: PM2
Mode: fork
User: root
Deployment: Manual setup on VM
No containerization
No autoscaling
The service was running fine in steady state.
• Trigger: Server reboot during OS patching
• Impact: Application unavailable for 18 minutes
• Root Cause: PM2 not registered with systemd
• Resolution: Integrated PM2 with systemd and enabled process resurrection
The server was restarted as part of routine OS patching.
After the reboot:
The server came up successfully
SSH access was normal
But the backend application was down
API health checks failed
External traffic started returning errors
Running:
pm2 ls
It returned no running processes because the PM2 daemon was not started after reboot.
Application downtime until manual intervention
No auto-recovery mechanism
Increased MTTR
Hidden operational risk exposed
This exposed a design gap.
The issue was detected via failed API health checks after reboot.
There was no alert configured to monitor the PM2 daemon state.
Downtime lasted approximately 18 minutes until manual intervention restored the service.
PM2 had previously been started manually in an interactive shell session.
It was never registered with systemd.
As a result, it did not start automatically after reboot.
There was:
No systemd integration
No startup registration
No process resurrection configuration
On reboot:
System Boot
↓
No PM2 daemon started
↓
No Node process started
↓
Application Down
This was not a runtime failure.
This was a lifecycle management design failure.
I redesigned the startup flow to align with production expectations.
Ensure that:
PM2 daemon starts automatically on boot
Saved processes are restored
No manual intervention required
pm2 startup
This generated a systemd unit file:
/etc/systemd/system/pm2-root.service
And enabled it:
systemctl enable pm2-root
pm2 save
This created:
/root/.pm2/dump.pm2
Without this file, resurrection would not occur.
Initially:
systemctl status pm2-root
Showed:
inactive (dead)
Meaning PM2 was still running from shell, not systemd.
Corrected by:
pm2 kill
systemctl start pm2-root
Now:
Active: active (running)
System Boot
↓
systemd
↓
pm2-root.service
↓
pm2 resurrect
↓
Node Application Starts
Server reboot was performed.
Post-reboot validation:
pm2 ls
systemctl status pm2-root
Result:
Application automatically started
No manual intervention required
The application started automatically without manual intervention, reducing MTTR for reboot-related events to near zero.
If the systemd integration failed:
• Disable pm2-root service
• Manually start PM2 using pm2 start
• Validate application health endpoint
• Restore previous working state
This ensured there was a recovery path during configuration changes.
• Standardized server bootstrap process to register PM2 with systemd
• Added reboot validation checklist after OS patching
• Integrated service state checks into monitoring alerts
• Planned migration to a dedicated service user
• Documented lifecycle management requirements
PM2 was configured under root.
Risk:
Larger blast radius in case of compromise
Principle of least privilege was violated.
Future improvement:
Systemd environment path contained:
/root/.nvm/versions/node/...
Risk:
Node version changes may break startup
NVM is not ideal for production servers
Better design:
Install Node globally
Lock version
In production systems, every long-running process must be supervised by the system init layer.
Running does not imply lifecycle management.
If the init system does not supervise your process, you do not have a resilient system.
The failure was not due to Node. Not due to PM2. Not due to application code.
It was a lifecycle management design gap.