API Node stable à 512 Mo de RAM. Un mois plus tard : redémarrages PM2 toutes les quatre heures. On ajoute max_memory_restart=800M — boucle de redémarrage à quarante par minute pendant le pic de midi. Post-mortem : cache Map global indexé par userId sans éviction, introduit avec la fonctionnalité « sessions temporaires » — fuite lente, invisible en test de dix minutes.
Node ne rend pas la mémoire au système d'exploitation de façon agressive — heapUsed peut tromper après un garbage collector. Une fuite, c'est une croissance tendancielle sous charge représentative, pas un pic post-déploiement. Redémarrer chaque nuit masque le problème jusqu'au jour où l'intervalle ne suffit plus.
Surveiller avant de redémarrer
Mesurez ce qui compte :
| Métrique | Signal |
|---|---|
process.memoryUsage().heapUsed | Tendance après garbage collection |
| RSS | C'est le RSS que l'OOM killer observe |
| Event loop lag | Fuite + charge = latence boucle événements |
| Fréquence des pauses GC | Augmente si le tas grossit |
Outils courants :
- Prometheus avec node_exporter et métriques applicatives
/metrics - PM2
pm2 monit,--max-memory-restartcouplé à une alerte si le compteur de redémarrages monte - APM (Datadog, etc.) pour corréler heap et requêtes lentes
Règle pratique : un redémarrage planifié masque — fixez une alerte RSS > baseline × 1,5 pendant une heure avant d'ajouter un cron nocturne.
Redémarrer sans graphique, c'est payer l'ambulance sans diagnostic.
Diagnostic heap
Procédure en staging, puis en production hors pic :
- Reproduire une charge longue (k6 pendant deux heures minimum).
- Capturer un heap snapshot au début et à la fin —
--inspectou--heapsnapshot-near-heap-limit(Node 18+). - Comparer dans Chrome DevTools (Comparison) — objets en augmentation de count.
- Remonter les retainers (Closure, Array, Buffer).
Causes fréquentes sur API web :
setIntervaljamais annulé avecclearIntervalsocket.ondupliqué sansremoveListener- Objet global
{ [reqId]: largeObject }jamais supprimé - Bibliothèque de logging gardant des références de contexte
Hébergement et limites mémoire
En conteneur ou Kubernetes, la limite mémoire tue Node par SIGKILL sans arrêt gracieux. Dimensionnez la limite au-dessus du heap maximum plus une marge pour le code natif — ou corrigez la fuite.
Sur VPS, le swap masque puis tue les performances — alertez sur le RSS, pas seulement le CPU. Un processus Node unique : la fuite affecte tout le service. Le mode cluster PM2 ne corrige pas la fuite — il la multiplie sur plusieurs workers.
Comparez les offres VPS adaptées Node via l'annuaire et le comparateur. Pour le déploiement, croisez avec PM2 en production.
PM2 : redémarrage versus correction
max_memory_restart : acceptable temporairement avec un ticket d'investigation prioritaire.
Redémarrage cron nocturne : tolérable si aucune fuite connue (bizarrerie de retour mémoire au OS) — pas un substitut à la correction.
Associez max_restarts et exp_backoff_restart_delay pour éviter les boucles de redémarrage qui coupent les WebSocket en plein pic.
Le sommet : le redémarrage automatique est une dette qui s'accumule
Voici ce que PM2 masque quand tout semble stable.
Culture d'équipe : heap snapshot avant d'ajouter max_memory_restart — ou au minimum un ticket de cause racine daté.
Décider et avancer sans angle mort
Avant d'ajouter un cron de redémarrage :
- Graphique RSS sur sept jours sous trafic normal — identifiez la tendance, pas les pics isolés.
- Alerte sur le compteur de redémarrages PM2 — pas seulement sur la disponibilité HTTP.
- Test de charge long en staging — deux heures minimum pour révéler une fuite lente.
- Corrigez les retainers identifiés dans le snapshot — Map sans TTL, listeners, timers.
- Retirez le redémarrage cron si la courbe mémoire se stabilise après correction.
Consultez PM2 déploiement pour les bonnes pratiques de supervision, et l'annuaire pour choisir un VPS avec assez de RAM pour capturer un heap snapshot hors pic.
Questions fréquentes
Comment détecter une fuite mémoire Node.js ?
Observez RSS ou heapUsed sur plusieurs heures ou jours sous trafic stable. Si la courbe monte sans redescendre après garbage collection, fuite probable. Un pic puis une chute est normal ; un plateau ascendant ne l'est pas.
PM2 max_memory_restart suffit-il ?
C'est un palliatif — recycle avant l'OOM mais perd l'état, masque la cause racine et peut boucler. Couplez-le à une alerte et une investigation heap avant de considérer le problème résolu.
heapdump en production ?
Possible hors pic avec --heapsnapshot-near-heap-limit (Node 18+) ou module heapdump déclenché manuellement. Analysez dans Chrome DevTools — évitez en plein pic sans mesurer l'impact.
Fuites classiques sur API Node web ?
Closures gardant des références de requêtes, écouteurs d'événements non retirés, cache Map sans durée de vie, buffers agrégés, timers oubliés — chacun laisse une courbe RSS qui monte lentement.
Surveiller la courbe mémoire coûte moins qu'un PM2 qui redémarre en boucle le jour où la fuite rattrape le cron.