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-envest requis lors du redémarrage pour récupérer de nouvelles variables d'environnement- N'utilisez jamais
pm2 restart allsans--update-envaprès la mise à jour de.env.localwatch: falseen production — la surveillance des fichiers provoque des boucles de redémarrage infinies avec les sorties de buildmax_memory_restartfournit une protection automatique contre les fuites de mémoire sans tuer le processus de manière permanentenode_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êmepm2 saveetpm2 startupconservent 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.
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.
Articles connexes
AWS EC2 Deployment Guide for Web Applications
Complete AWS EC2 deployment guide: instance selection, security groups, Node.js deployment, Nginx reverse proxy, SSL, auto-scaling, CloudWatch monitoring, and cost optimization.
Zero-Downtime Database Migrations with Drizzle ORM
Run database migrations without downtime using Drizzle ORM. Covers expand-contract pattern, backward-compatible schema changes, rollback strategies, and CI/CD integration for PostgreSQL.
Next.js 16 App Router: Production Patterns and Pitfalls
Production-ready Next.js 16 App Router patterns: server components, caching strategies, metadata API, error boundaries, and performance pitfalls to avoid.