PM2 Process Management for Node.js in Production

PM2 process management for Node.js production: ecosystem file configuration, zero-downtime restarts, log management, monitoring, clustering, and deployment workflows.

E
ECOSIRE Research and Development Team
|19 mars 202610 min de lecture2.1k Mots|

Gestion des processus PM2 pour Node.js en production

Lorsque votre application Node.js plante à 2 heures du matin, PM2 fait la différence entre son redémarrage automatique et le fait que vos utilisateurs voient une page vierge jusqu'à votre réveil. PM2 est un gestionnaire de processus éprouvé qui gère les redémarrages automatiques, le clustering pour une utilisation multicœur, l'agrégation de journaux et les déploiements sans temps d'arrêt, le tout avec un seul fichier de configuration résidant dans votre référentiel.

Ce guide couvre une configuration de production PM2 gérant simultanément 5 processus Node.js : Next.js (frontend), NestJS (API), Docusaurus (docs) et deux sites de marque. Les modèles s’appliquent également aux déploiements à processus unique.

Points clés à retenir

  • Le fichier ecosystem.config.cjs (CommonJS, pas .js) fonctionne à la fois avec les projets ESModule et CommonJS
  • L'indicateur --update-env est requis lors du redémarrage pour récupérer de nouvelles variables d'environnement
  • N'utilisez jamais pm2 restart all sans --update-env après la mise à jour de .env.local
  • watch: false en production — la surveillance des fichiers provoque des boucles de redémarrage infinies avec les sorties de build
  • max_memory_restart fournit une protection automatique contre les fuites de mémoire sans tuer le processus de manière permanente
  • node_args: '--max-old-space-size=4096' empêche les plantages du MOO lors d'opérations gourmandes en mémoire
  • Les journaux PM2 tournent avec le module pm2-logrotate — installez-le immédiatement après PM2 lui-même
  • pm2 save et pm2 startup conservent votre liste de processus lors des redémarrages du serveur

##Installation

# Install PM2 globally
npm install -g pm2

# Install the log rotation module immediately
pm2 install pm2-logrotate

# Configure log rotation
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD

Fichier de configuration de l'écosystème

Le fichier ecosystem.config.cjs (format CommonJS pour fonctionner à la fois avec les projets ESM et CJS) définit tous vos processus :

// ecosystem.config.cjs
module.exports = {
  apps: [
    // ─── Next.js Frontend ────────────────────────────────────────────
    {
      name: 'ecosire-web',
      script: 'node_modules/.bin/next',
      args: 'start',
      cwd: '/opt/ecosire/app/apps/web',
      instances: 1,        // Single instance — Next.js handles its own multi-threading
      exec_mode: 'fork',
      env: {
        NODE_ENV: 'production',
        PORT: 3000,
      },
      // Memory management
      max_memory_restart: '1G',
      node_args: '--max-old-space-size=1024',
      // Logging
      out_file: '/var/log/pm2/ecosire-web.out.log',
      error_file: '/var/log/pm2/ecosire-web.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      // Restart behavior
      watch: false,
      restart_delay: 3000,
      max_restarts: 10,
      min_uptime: '30s',   // Must stay up 30s to count as successful start
      autorestart: true,
      // Graceful shutdown
      kill_timeout: 30000, // 30 seconds to shut down gracefully
      wait_ready: true,    // Wait for process.send('ready')
      listen_timeout: 60000,
    },

    // ─── NestJS API ──────────────────────────────────────────────────
    {
      name: 'ecosire-api',
      script: 'dist/main.js',
      cwd: '/opt/ecosire/app/apps/api',
      instances: 2,         // Cluster mode for multi-core utilization
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'production',
        PORT: 3001,
      },
      max_memory_restart: '512M',
      node_args: '--max-old-space-size=512',
      out_file: '/var/log/pm2/ecosire-api.out.log',
      error_file: '/var/log/pm2/ecosire-api.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      watch: false,
      restart_delay: 2000,
      max_restarts: 10,
      min_uptime: '20s',
      autorestart: true,
      kill_timeout: 15000,
      // Graceful cluster reload support
      listen_timeout: 30000,
    },

    // ─── Docusaurus Docs ─────────────────────────────────────────────
    {
      name: 'ecosire-docs',
      script: 'node_modules/.bin/docusaurus',
      args: 'serve',
      cwd: '/opt/ecosire/app/apps/docs',
      instances: 1,
      exec_mode: 'fork',
      env: {
        NODE_ENV: 'production',
        PORT: 3002,
      },
      max_memory_restart: '256M',
      out_file: '/var/log/pm2/ecosire-docs.out.log',
      error_file: '/var/log/pm2/ecosire-docs.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      watch: false,
      restart_delay: 3000,
      max_restarts: 5,
      min_uptime: '30s',
      autorestart: true,
      kill_timeout: 10000,
    },

    // ─── Brand Site: Odovation ───────────────────────────────────────
    {
      name: 'odovation-web',
      script: 'node_modules/.bin/next',
      args: 'start',
      cwd: '/opt/ecosire/app/apps/odovation',
      instances: 1,
      exec_mode: 'fork',
      env: {
        NODE_ENV: 'production',
        PORT: 3010,
      },
      max_memory_restart: '512M',
      out_file: '/var/log/pm2/odovation-web.out.log',
      error_file: '/var/log/pm2/odovation-web.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      watch: false,
      restart_delay: 3000,
      max_restarts: 10,
      min_uptime: '30s',
      autorestart: true,
    },

    // ─── Brand Site: MuhammadAmir ────────────────────────────────────
    {
      name: 'muhammadamir-web',
      script: 'node_modules/.bin/next',
      args: 'start',
      cwd: '/opt/ecosire/app/apps/muhammadamir',
      instances: 1,
      exec_mode: 'fork',
      env: {
        NODE_ENV: 'production',
        PORT: 3020,
      },
      max_memory_restart: '512M',
      out_file: '/var/log/pm2/muhammadamir-web.out.log',
      error_file: '/var/log/pm2/muhammadamir-web.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      watch: false,
      restart_delay: 3000,
      max_restarts: 10,
      min_uptime: '30s',
      autorestart: true,
    },
  ],
};

Commandes principales PM2

# Start all processes from ecosystem file
pm2 start ecosystem.config.cjs

# Restart all (with updated environment variables)
pm2 restart ecosystem.config.cjs --update-env

# Graceful reload (zero-downtime for cluster mode)
pm2 reload ecosystem.config.cjs

# Stop all processes
pm2 stop all

# Delete all processes from PM2 registry
pm2 delete all

# Individual process management
pm2 restart ecosire-api
pm2 stop ecosire-docs
pm2 logs ecosire-web --lines 100

# Real-time monitoring dashboard
pm2 monit

# Status overview
pm2 status
pm2 list

Démarrage au redémarrage du serveur

Sans configuration de démarrage, tous les processus PM2 sont perdus au redémarrage du serveur :

# Generate and install the startup script for your init system
pm2 startup
# Copy the output command and run it (it looks like:)
# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u ubuntu --hp /home/ubuntu

# Save the current process list
pm2 save
# This creates ~/.pm2/dump.pm2 — processes are restored on reboot

# Verify startup works
pm2 resurrect  # Manually restore from dump.pm2

Chaque fois que vous ajoutez ou supprimez des processus, exécutez à nouveau pm2 save pour mettre à jour le fichier de vidage.


Déploiements sans temps d'arrêt

Pour NestJS en mode cluster, PM2 prend en charge de véritables rechargements sans temps d'arrêt :

# Reload restarts workers one at a time (zero-downtime)
# Old workers handle requests while new ones start
pm2 reload ecosire-api

# vs restart — kills all workers simultaneously (brief downtime)
pm2 restart ecosire-api

Pour Next.js (qui s'exécute en mode fork, instance unique), le temps d'arrêt nul nécessite une approche différente. Utilisez la configuration wait_ready + listen_timeout avec un signal de démarrage de votre application :

// apps/web — this is handled automatically by Next.js
// But for NestJS, send the ready signal explicitly:

// apps/api/src/main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3001);

  // Signal PM2 that the process is ready
  if (process.send) {
    process.send('ready');
  }
}

bootstrap();

Gestion des journaux

Les journaux PM2 peuvent remplir votre disque s'ils ne sont pas gérés. Configurez immédiatement la rotation des journaux :

# Install log rotation module
pm2 install pm2-logrotate

# Configuration
pm2 set pm2-logrotate:max_size 50M       # Rotate when log reaches 50MB
pm2 set pm2-logrotate:retain 7           # Keep 7 days of logs
pm2 set pm2-logrotate:compress true      # Gzip rotated logs
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD
pm2 set pm2-logrotate:workerInterval 30  # Check rotation interval (seconds)
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'  # Daily at midnight

Commandes de journal utiles :

# View all logs combined
pm2 logs

# View specific process logs
pm2 logs ecosire-api

# View with timestamps
pm2 logs --timestamp

# Flush all log files
pm2 flush

# Tail error logs only
pm2 logs ecosire-api --err --lines 200

Surveillance et métriques

PM2 Plus (anciennement Keymetrics) fournit une surveillance basée sur le cloud. Pour la surveillance auto-hébergée :

# Built-in terminal dashboard
pm2 monit

# Get JSON status for scripting/monitoring integration
pm2 jlist    # JSON process list
pm2 prettylist  # Formatted process list

# Integrate with your monitoring stack
pm2 set pm2-server-monit:interval 5  # Metrics collection interval

Pour la surveillance de la production, exposez les métriques PM2 à Prometheus :

npm install -g pm2-prometheus-exporter
pm2 set pm2-prometheus-exporter:port 9209

# Scrape in Prometheus config:
# - job_name: pm2
#   static_configs:
#     - targets: ['localhost:9209']

Intégration du script de déploiement

Une séquence de déploiement typique :

#!/bin/bash
# scripts/deploy-production.sh

set -e

echo "=== Starting deployment ==="

# 1. Pull latest code
git pull origin main

# 2. Install dependencies
pnpm install --frozen-lockfile

# 3. Build all apps (with Turbo remote cache)
TURBO_TOKEN="$TURBO_TOKEN" TURBO_TEAM="$TURBO_TEAM" \
  npx turbo run build

# 4. Run database migrations
pnpm --filter @ecosire/db db:migrate

# 5. Restart PM2 processes
# --update-env picks up changes in .env.local
pm2 restart ecosystem.config.cjs --update-env

# 6. Wait for processes to stabilize
sleep 10

# 7. Health checks
curl -f https://ecosire.com/ -o /dev/null -s || {
  echo "Web health check failed — rolling back"
  git revert HEAD --no-edit
  pm2 restart ecosystem.config.cjs --update-env
  exit 1
}

curl -f https://api.ecosire.com/api/health -o /dev/null -s || {
  echo "API health check failed — rolling back"
  git revert HEAD --no-edit
  pm2 restart ecosystem.config.cjs --update-env
  exit 1
}

# 8. Save process state
pm2 save

echo "=== Deployment complete ==="

Pièges courants et solutions

Piège 1 : Oublier --update-env

Après la mise à jour de .env.local, l'exécution de pm2 restart all sans --update-env entraîne le redémarrage des processus avec les anciennes variables d'environnement. Utilisez toujours pm2 restart ecosystem.config.cjs --update-env.

Piège 2 : Utiliser watch: true en production

watch: true redémarre le processus lorsqu'un fichier est modifié. En production, les résultats de build changent à chaque déploiement, ce qui provoque des boucles de redémarrage infinies. Définissez toujours watch: false.

Piège 3 : Ne pas gérer SIGTERM pour un arrêt progressif

PM2 envoie SIGTERM lors du redémarrage/arrêt. Si votre application ne le gère pas, PM2 attend kill_timeout millisecondes et envoie SIGKILL, ce qui peut entraîner la perte de requêtes. Gérez SIGTERM dans NestJS :

// main.ts
const app = await NestFactory.create(AppModule);
await app.listen(3001);

// Graceful shutdown
process.on('SIGTERM', async () => {
  await app.close();
  process.exit(0);
});

Piège 4 : manque d'espace disque pour les journaux PM2

Sans pm2-logrotate, les journaux PM2 augmentent indéfiniment. Une API à fort trafic peut générer des gigaoctets de journaux par jour. Installez pm2-logrotate immédiatement et définissez un max_size (50 Mo) et un retain (7 jours) raisonnables.

Piège 5 : perte de processus après le redémarrage

pm2 start ne conserve pas les processus lors des redémarrages. Exécutez toujours pm2 startup + pm2 save après la configuration initiale. Si les processus disparaissent après un redémarrage, exécutez pm2 resurrect pour restaurer à partir du dump enregistré.


Questions fréquemment posées

Quand dois-je utiliser le mode cluster ou le mode fork ?

Utilisez le mode cluster pour les charges de travail liées au processeur (API NestJS avec calculs lourds, traitement des données). Le mode cluster génère des processus de travail instances et des équilibres de charge PM2 entre eux, en exploitant tous les cœurs de processeur. Utilisez le mode fork pour les charges de travail liées aux E/S (Next.js, service de fichiers statiques) ou lorsque le processus ne prend pas en charge le clustering (scripts monothread, service Docusaurus). Next.js gère ses propres threads de travail en interne, donc le mode fork avec instances: 1 est correct.

Comment exécuter PM2 dans un conteneur Docker ?

PM2 dans Docker utilise pm2-docker (ou pm2-runtime) au lieu de pm2 pour gérer correctement les signaux. La version d'exécution ne démonise pas (ce qui entraînerait la fermeture de Docker), transmet correctement les signaux aux processus enfants et se connecte à stdout/stderr au lieu de fichiers. Utilisez CMD ["pm2-runtime", "ecosystem.config.cjs"] dans votre Dockerfile.

Comment surveiller les processus PM2 à partir d'une machine distante ?

PM2 Plus (service cloud payant) fournit un tableau de bord Web. Pour une surveillance auto-hébergée, exposez les métriques de PM2 via l'exportateur Prometheus et visualisez-les dans Grafana. Pour des vérifications d'état simples, vous pouvez SSH et exécuter pm2 status, ou exposer les métriques via un point de terminaison HTTP interrogé par votre système de surveillance.

Quelle est la différence entre le rechargement pm2 et le redémarrage de pm2 ?

pm2 restart tue tous les travailleurs simultanément et les redémarre - il y a une brève période sans travailleurs en cours d'exécution (temps d'arrêt). pm2 reload est gracieux : il démarre de nouveaux travailleurs, attend qu'ils soient prêts, puis arrête les anciens travailleurs – aucun temps d'arrêt. Utilisez pm2 reload pour les déploiements de production. Remarque : le rechargement ne fonctionne correctement qu'en mode cluster ; le mode fork revient au comportement de redémarrage.

Comment définir différentes variables d'environnement pour différents processus ?

Chaque processus dans ecosystem.config.cjs possède ses propres sections env et env_production. La section env_production est utilisée lorsque vous transmettez --env production aux commandes PM2. Pour les secrets, ne les mettez jamais directement dans le fichier de l'écosystème - définissez-les dans l'environnement système ou dans le fichier .env.local et laissez PM2 en hériter. L'indicateur --update-env garantit que PM2 relit les variables d'environnement lors du redémarrage.


Prochaines étapes

PM2 est un élément fondamental de tout déploiement de production Node.js. ECOSIRE gère 5 processus PM2 en production — Next.js, NestJS, Docusaurus et deux sites de marque — avec des redémarrages automatiques, une rotation des journaux et des déploiements sans temps d'arrêt à chaque poussée vers le principal.

Que vous ayez besoin d'une assistance technique DevOps, d'une architecture de déploiement de production ou d'une aide pour migrer vers une configuration conteneurisée, découvrez nos services pour voir comment nous pouvons vous aider.

E

Rédigé par

ECOSIRE Research and Development Team

Création de produits numériques de niveau entreprise chez ECOSIRE. Partage d'analyses sur les intégrations Odoo, l'automatisation e-commerce et les solutions d'entreprise propulsées par l'IA.

Discutez sur WhatsApp