treeru.com

Running Next.js in Production With PM2 — Zero-Downtime Deploys and Monitoring

Vercel makes deployment easy, but costs scale with traffic. When you self-host Next.js, PM2 is the de facto standard for production process management. It handles auto-restart on crashes, zero-downtime deployments, log management, and systemd integration — everything you need to run Node.js reliably on your own server.

Why PM2?

Running node server.js directly means a single unhandled error kills your entire service. PM2 wraps your Node.js process with automatic restart, multi-process clustering, log file management, and more.

# Install PM2 globally
npm install -g pm2

# Start a Next.js app
pm2 start npm --name "my-app" -- start

# Check status
pm2 status

# View real-time logs
pm2 logs my-app --lines 50
FeatureDirect nodePM2
Auto-restart on crashNoAutomatic
Multi-processManual implementationCluster mode built-in
Log managementstdout onlyFile-based with rotation
Zero-downtime deployNoreload command
Server reboot recoveryManual startsystemd integration

ecosystem.config.js — Centralized Configuration

Instead of typing CLI options every time, put all configuration in an ecosystem.config.js file. This is especially useful when running multiple apps on the same server.

// ecosystem.config.js
module.exports = {
  apps: [
    {
      name: "main-site",
      script: "node_modules/.bin/next",
      args: "start",
      cwd: "/home/deploy/main-site",
      instances: 1,          // Fork mode for Next.js
      exec_mode: "fork",
      env: {
        NODE_ENV: "production",
        PORT: 3000,
      },
      max_memory_restart: "500M",
      error_file: "./logs/error.log",
      out_file: "./logs/output.log",
      merge_logs: true,
      log_date_format: "YYYY-MM-DD HH:mm:ss",
    },
    {
      name: "admin-site",
      script: "node_modules/.bin/next",
      args: "start",
      cwd: "/home/deploy/admin-site",
      instances: 1,
      exec_mode: "fork",
      env: {
        NODE_ENV: "production",
        PORT: 3001,
      },
      max_memory_restart: "300M",
    },
  ],
};
# Start all apps defined in ecosystem file
pm2 start ecosystem.config.js

# Start a specific app only
pm2 start ecosystem.config.js --only main-site

# Apply configuration changes
pm2 reload ecosystem.config.js

Always set max_memory_restart. Next.js apps can gradually increase memory usage over time. This setting auto-restarts the process before it hits OOM, preventing crashes.

Cluster Mode vs Fork Mode

PM2's cluster mode spawns worker processes equal to your CPU core count, distributing requests across them. However, Next.js has its own internal multi-threading, which changes the recommendation.

FactorFork ModeCluster Mode
Process count1CPU core count
Load balancingNoneBuilt-in round-robin
Zero-downtime reloadBrief interruptionTruly zero-downtime
Next.js compatibilityPerfectCaution needed
Memory usageLowMultiplied by core count

For Next.js, fork mode is recommended. Next.js handles multi-threading internally. Running it in PM2 cluster mode can cause port conflicts and ISR cache inconsistencies. If you need multiple instances, use your reverse proxy (Caddy or Nginx) for load balancing instead.

reload vs restart — Zero-Downtime Deployment

The difference between pm2 restart and pm2 reload is critical for production:

CommandBehaviorDowntimeUse Case
restartKill process, then start new one2-5 secondsConfig changes, emergency recovery
reloadStart new process, wait until ready, then kill old0 seconds (cluster mode)Regular deployments
stop + startFull stop, then startSeconds to minutesMaintenance, migrations
# restart: kills existing process → starts new (2-5s downtime)
pm2 restart my-app

# reload: starts new → waits for ready → kills old (0s downtime in cluster)
pm2 reload my-app

# With environment variable update
pm2 reload my-app --update-env

Always use reload in deployment scripts. Make it the default in your CI/CD pipeline to prevent accidental downtime. Note that true zero-downtime reload only works in cluster mode — in fork mode, reload behaves identically to restart with a brief interruption.

Log Management With pm2-logrotate

PM2 writes logs to files by default. Without rotation, these files grow indefinitely until your disk fills up — causing an embarrassing outage. Log rotation is mandatory for production.

# Install the logrotate module
pm2 install pm2-logrotate

# Configure rotation settings
pm2 set pm2-logrotate:max_size 10M     # Max file size before rotation
pm2 set pm2-logrotate:retain 7         # Keep 7 rotated files
pm2 set pm2-logrotate:compress true    # Gzip old log files
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm
pm2 set pm2-logrotate:rotateModule true

# Check log file locations
pm2 logs --nostream
SettingRecommendedDescription
max_size10MRotate when file exceeds this size
retain7Keep only the 7 most recent log files
compresstrueGzip rotated logs to save disk space
workerInterval30Check log size every 30 seconds

Set up logrotate immediately after installing PM2. Logs growing to tens of GB and filling the disk is one of the most common — and most preventable — production failures.

systemd Integration and Auto-Restart

When the server reboots, PM2 processes disappear. The pm2 startup command registers a systemd service that automatically starts PM2 and restores all previously running apps on boot.

# 1. Generate the startup script
pm2 startup systemd
# Copy and run the sudo command it outputs

# 2. Save current process list
pm2 save

# 3. Verify after reboot
sudo reboot
# ... after reboot ...
pm2 status   # All previous processes are running

# Check systemd service status
systemctl status pm2-deploy

Don't forget pm2 save after adding or modifying apps. This command writes the current process list to disk. On reboot, PM2 reads this saved list and restores exactly what was running.

Useful Monitoring Commands

# Real-time dashboard (CPU, memory, logs)
pm2 monit

# Detailed process info
pm2 show my-app

# Track memory usage
pm2 show my-app | grep memory

# Check restart count (high restarts = problem)
pm2 show my-app | grep restarts

Summary Checklist

  • Use ecosystem.config.js to manage all app configurations in one file
  • Use fork mode for Next.js — cluster mode causes ISR cache and port issues
  • Use reload instead of restart for deployments (true zero-downtime in cluster mode only)
  • Install pm2-logrotate immediately — prevent disk-full outages with automatic log rotation
  • Run pm2 startup + pm2 save for automatic recovery after server reboots
  • Set max_memory_restart to prevent OOM kills from gradual memory leaks

PM2 turns a bare Node.js process into a production-grade service with auto-restart, zero-downtime deploys, log management, and boot persistence. For self-hosted Next.js, it's the simplest path to reliable production operations without Vercel's pricing.