Node.js + PM2 : apps Node en production

Node.js + PM2 : apps Node en production

Deployer Node.js en prod necessite plus que `node index.js`. PM2 gere les processus, le cluster mode, les restarts automatiques, le log et le monitoring. La solution standard pour exposer une app Node.js de facon robuste.

Introduction

Node.js seul est mono-process. En prod, on veut :

  • Plusieurs workers (cluster) pour utiliser tous les CPU
  • Restart automatique en cas de crash
  • Demarrage au boot
  • Logs centralises et rotates
  • Monitoring (CPU, RAM, latence)
  • Zero-downtime reload

PM2 est l'outil de reference pour tout ca. Open-source, mature, riche en plugins.

Prerequis

  • VPS Linux Debian / Ubuntu
  • Node.js 18+ installe
  • Acces root
  • Une app Node.js (Express, NestJS, Next.js, Fastify, etc.)

Etape 1 : Installation Node.js (NodeSource)

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node --version
npm --version

Etape 2 : Installation PM2

sudo npm install -g pm2
pm2 --version

Etape 3 : Demarrer une app

Imaginons /var/www/monapp/index.js :

const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello'));
app.listen(3000, () => console.log('Listening on 3000'));

Demarrer avec PM2 :

cd /var/www/monapp
pm2 start index.js --name monapp

Voir :

pm2 list

Status, CPU, RAM, restarts.

Etape 4 : Cluster mode

Pour utiliser tous les CPU :

pm2 start index.js --name monapp -i max

-i max lance autant d'instances que de cores. Ou nombre fixe :

pm2 start index.js --name monapp -i 4

PM2 lance 4 workers Node, distribue les requetes en round-robin. Chaque worker tourne sur un core different.

Etape 5 : Demarrage au boot

pm2 startup

PM2 affiche une commande a executer en root, type :

sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u admin --hp /home/admin

Executez-la.

Puis sauvegardez la liste des apps en cours :

pm2 save

Au prochain reboot, PM2 redemarrera toutes vos apps automatiquement.

Etape 6 : Logs

pm2 logs              # tous les logs
pm2 logs monapp       # une app
pm2 logs --lines 100  # 100 dernieres lignes

Localisation des fichiers :

~/.pm2/logs/monapp-out.log    # stdout
~/.pm2/logs/monapp-error.log  # stderr

Rotation automatique (recommandee) :

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 100M
pm2 set pm2-logrotate:retain 30
pm2 set pm2-logrotate:compress true

Etape 7 : Configuration via ecosystem.config.js

Plutot que des arguments CLI, centralisez la config :

nano /var/www/monapp/ecosystem.config.js
module.exports = {
  apps: [{
    name: 'monapp',
    script: './index.js',
    instances: 'max',
    exec_mode: 'cluster',
    autorestart: true,
    watch: false,
    max_memory_restart: '500M',
    env: {
      NODE_ENV: 'production',
      PORT: 3000,
      DATABASE_URL: 'postgres://localhost/monapp'
    },
    error_file: '/var/log/pm2/monapp-error.log',
    out_file: '/var/log/pm2/monapp-out.log',
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z'
  }]
}

Lancer :

pm2 start ecosystem.config.js
pm2 save

Etape 8 : Zero-downtime reload

pm2 reload monapp

PM2 redemarre les workers un par un, sans perdre une seule requete. Beaucoup mieux que restart (qui kill tout d'un coup).

Utilisez-le pour les deploiements :

git pull
npm install
pm2 reload monapp

Etape 9 : Reverse proxy Nginx devant

upstream monapp_backend {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 443 ssl http2;
    server_name monapp.fr;
    
    location / {
        proxy_pass http://monapp_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Etape 10 : Monitoring temps reel

pm2 monit

Interface TUI : CPU, RAM, logs, par instance. Pratique pour surveiller plusieurs apps.

Ou PM2 Plus (SaaS) pour monitoring distance :

pm2 link <secret_key> <public_key>

Etape 11 : Variables d'environnement

Plusieurs facons :

// ecosystem.config.js
env_production: {
    NODE_ENV: 'production',
    PORT: 3000
},
env_staging: {
    NODE_ENV: 'staging',
    PORT: 3001
}
pm2 start ecosystem.config.js --env production

Ou via .env file et dotenv dans votre app.

Etape 12 : Memory leaks

Si votre app fuit de la memoire :

max_memory_restart: '500M'

PM2 restart automatiquement le worker des qu'il depasse 500 Mo. Workaround temporaire en attendant de fixer la fuite.

Pour identifier :

pm2 reload monapp --update-env
pm2 show monapp

Voir Heap snapshots via Chrome DevTools, ou utilisez clinic.js.

Depannage

App crash en boucle

pm2 logs monapp --err --lines 50

Identifiez l'erreur. Souvent : module manquant, port deja utilise, variable d'env manquante.

"ENOMEM" out of memory

Le worker depasse max_memory_restart. Augmentez ou identifiez la fuite.

Cluster mode ne fonctionne pas

L'app doit pouvoir tourner en cluster :

  • Pas d'etat partage en memoire (utilisez Redis)
  • WebSockets : utilisez socket.io-redis ou un mode sticky

PM2 ne demarre pas au boot

pm2 unstartup
pm2 startup
pm2 save

Logs trop volumineux

pm2 set pm2-logrotate:max_size 50M
pm2 flush  # vider les logs

Commandes utiles

pm2 list                          # toutes les apps
pm2 show monapp                   # detail une app
pm2 logs monapp --lines 100       # logs
pm2 monit                         # TUI temps reel
pm2 restart monapp                # restart hard
pm2 reload monapp                 # restart zero-downtime
pm2 stop monapp                   # stop
pm2 delete monapp                 # supprimer
pm2 save                          # sauvegarder la liste
pm2 resurrect                     # restaurer depuis sauvegarde
pm2 flush                         # vider tous les logs
pm2 startup                       # auto-start au boot
pm2 update                        # MAJ PM2 sans downtime
pm2 ecosystem                     # generer un ecosystem.config.js

Conclusion

PM2 vous donne :

  • Cluster mode pour exploiter tous les CPU
  • Restart automatique sur crash ou memoire excessive
  • Zero-downtime deployments via reload
  • Logs centralises et rotates
  • Demarrage au boot

Pour aller plus loin :

  • Utilisez PM2 Plus pour le monitoring distance
  • Migrez vers systemd + Node.js cluster module pour une approche plus systeme
  • Pour les architectures serverless, considerez AWS Lambda ou Cloudflare Workers

Ressources

Rejoignez notre serveur communautaire Discord

Pour toute question, suggestion ou simplement pour discuter avec la communauté, rejoignez-nous sur Discord !

900+Membres