Aller au contenu principal
Site en cours de refonte — quelques pages peuvent bouger ou évoluer.
15 mars 2026

Next.js 16 en dev qui mange toute la RAM : diagnostiquer (vraiment) avant de “juste ajouter 32 Go”

Quand Next.js 16 finit en swap en dev, ce n’est pas un “mystère”. Tu peux isoler la cause, mesurer, et appliquer des mitigations propres.

10 min de lecture
8 vues
réactions
Partager :
Next.js 16 en dev qui mange toute la RAM : diagnostiquer (vraiment) avant de “juste ajouter 32 Go”

Quand ton serveur Next.js 16 en dev grimpe à 10, 14, 22 Go de RAM jusqu’à mettre la machine à genoux, il y a deux réactions classiques. La première c’est le déni, genre « c’est Chrome ». La deuxième c’est le bricolage, genre « bon, je mets 32 Go et on n’en parle plus ».

La vérité, c’est que la RAM qui explose n’est pas une “vibe”. C’est un pipeline qui grossit trop vite, ou qui ne redescend jamais. Et tant que tu n’as pas isolé ce qui grossit (Turbopack, watcher, graph de modules, cache, fuite), tu ne fais que traiter les symptômes. On va faire du triage, puis du concret.

Le point de départ : “100% RAM”, ça veut dire quoi exactement ?

Déjà, on se met d’accord sur ce qu’on mesure. Sur macOS, le moniteur d’activité te donne un ressenti. Sur Linux, tu vas vite voir le swap s’allumer. Mais ce qui nous intéresse, c’est la mémoire du process Node qui fait tourner le dev server.

Ce que je veux savoir, c’est : est-ce que tu as une montée continue (ça sent la fuite ou un cache sans limite), ou des paliers (ça sent plutôt le module graph qui s’élargit au fil des navigations, ou des recompilations qui empilent) ? Et est-ce que ça arrive juste au démarrage, ou après 15 minutes de dev avec HMR, navigations, changements de fichiers ?

Avant de toucher au code, commence par rendre la mesure triviale. Lance ton dev server et garde un œil sur l’empreinte mémoire du process. Rien de plus. Si tu ne peux pas la voir clairement, tu vas rationaliser n’importe quoi.

Isoler le coupable : Turbopack, watcher, ou graph de modules

La discussion “Next.js 16 mange toute la RAM” tourne souvent en rond parce que tout le monde parle d’un problème différent. Il y a au moins trois familles.

Première famille : le bundler et son cache (Turbopack ou Webpack). Les dev servers modernes cachent beaucoup pour être rapides. Si le cache grossit sans garde-fou, ou si certains patterns de code le font exploser (re-exports massifs, modules qui se réimportent en cascade), tu payes en RAM.

Deuxième famille : le système de fichiers et ses watchers. Monorepo, node_modules énormes, packages locaux symlinkés, dossiers générés, répertoires build qui se font watch par accident. Un watcher qui observe trop large peut déclencher trop de rebuilds, et garder des états en mémoire plus longtemps que prévu.

Troisième famille : la taille du graph de dépendances. C’est le sujet que beaucoup sous-estiment. Un projet qui adore les “barrels” (index.ts partout), les re-exports et les importations “pratiques” peut créer un graph gigantesque. En dev, ça se voit plus qu’en prod. Le bundler doit comprendre et recoller davantage, plus souvent.

Ton job n’est pas de deviner. Ton job c’est de faire varier une seule chose et de voir si la RAM suit.

Basculer Turbopack / Webpack : le test qui tranche vite

Si tu es sur Next 16, tu es probablement tenté par Turbopack en dev. Très bien. Mais si ton objectif est de diagnostiquer, tu dois être capable de comparer.

Fais un essai en forçant l’autre mode de dev (selon ta config et ce que Next expose dans ta version). Si la RAM devient “normale” en Webpack mais part en orbite en Turbopack, tu as un signal fort. L’inverse est aussi possible, mais le but est le même : réduire le champ.

Ce test n’est pas là pour décider “Turbopack c’est nul”. Il est là pour te dire si tu dois chercher un pattern de code qui fait gonfler le graph (qui impactera les deux), ou un problème plus spécifique à un outil.

Mesurer côté Node : arrêter de raisonner à l’aveugle

Le réflexe “je mets --max-old-space-size” est utile, mais ce n’est pas un diagnostic. Pour comprendre ce qui se passe, il faut regarder le heap. Le plus simple, c’est d’activer l’inspector et de prendre des snapshots quand la RAM est basse, puis quand elle est haute.

# Lance Next en mode inspectable (à adapter selon ton script npm/pnpm/yarn)
NODE_OPTIONS="--inspect" next dev

Ensuite tu ouvres Chrome, tu vas sur chrome://inspect, tu attaches le process Node, et tu prends un Heap snapshot. Ce que tu cherches n’est pas “un objet mystérieux”. Tu cherches une tendance. Des strings qui s’accumulent, des maps énormes, des objets liés au bundler, ou des caches.

Si tu veux juste un compteur simple pendant que tu reproduis, logge la mémoire au fil du temps. C’est trivial et ça te donne un graphe mental très vite.

setInterval(() => {
  const m = process.memoryUsage();
  const mb = (n) => Math.round((n / 1024 / 1024) * 10) / 10;
  console.log(
    `[mem] rss=${mb(m.rss)}MB heapUsed=${mb(m.heapUsed)}MB heapTotal=${mb(m.heapTotal)}MB ext=${mb(m.external)}MB`
  );
}, 5000);

Tu ne vas pas garder ça dans ton app, évidemment. Mais pour une session de triage, c’est parfait. Tu vois si heapUsed monte, si c’est plutôt rss (mémoires natives, buffers), ou si tout suit. Et ça change complètement les hypothèses.

Le piège classique : les barrels et les re-exports qui gonflent le graph

Le “barrel file” est une drogue douce. Tu commences en te disant que c’est propre : un @/ui qui réexporte tout, puis un @/lib, puis un @/features, puis un @/shared. Et un jour tu as un import qui tire la moitié du repo, sans que personne ne s’en rende compte.

En prod, le tree-shaking peut masquer une partie du problème. En dev, tu payes l’analyse, la résolution, et la maintenance du graph à chaque changement. Et si tu mixes ça avec un monorepo et des packages internes qui se réexportent entre eux, la taille du graphe et le nombre de fichiers “touchés” à chaque rebuild peuvent exploser.

Tu n’as pas besoin de “supprimer tous les barrels”. Tu as besoin d’être honnête sur ceux qui coûtent. Typiquement, les barrels qui exportent des composants lourds, des icônes, des wrappers qui importent des CSS, ou des modules qui importent eux-mêmes des dizaines de fichiers. Ceux-là, je les casse sans état d’âme.

Watchers en monorepo : quand tu observes trop large, tu rebuilds trop

Le cas qui revient souvent en pratique, c’est le monorepo où le dev server watch plus que ce que tu crois. Dossiers build, packages internes non compilés, fichiers générés, doublons de dépendances. Le symptôme est vicieux : la RAM grimpe parce que tu déclenches plus de recompilations que nécessaire, et tu empiles des états de compilation.

Deux indices simples : tu changes un fichier “local”, et tu vois une recompilation massive ; ou bien tu ne touches à rien, et tu vois quand même des triggers. Là, ce n’est plus une question de “Node est gourmand”. C’est une question d’architecture de repo et de frontières entre packages.

Ce qui marche bien, c’est d’avoir des packages internes compilés (ou au moins avec une sortie stable), et de faire en sorte que Next consomme la sortie, pas les sources de tout le monde. Oui, ça ajoute une étape, mais ça évite de transformer Next dev en indexeur global du monorepo.

Mitigations utiles (pas glamour, mais ça te rend ton laptop)

On peut être très “puriste” et dire qu’il faut corriger la cause. Je suis d’accord. Mais quand tu as un projet à livrer et que le dev server te bloque, tu as besoin d’un plan de survie.

La mitigation la plus simple, c’est de mettre une limite mémoire à Node pour éviter le scénario “ça mange tout, puis ça swap, puis tout le monde souffre”. Ça ne règle pas la fuite, mais ça te redonne une machine utilisable, et ça peut transformer un freeze total en crash rapide et reproductible, donc en bug report exploitable.

Autre mitigation très concrète : réduire le graph là où il gonfle pour rien. Casser un barrel toxique, remplacer deux imports “magiques” par des imports directs, scinder un package interne qui exporte trop. Ce n’est pas sexy, mais quand tu vois la RAM baisser après un seul changement, tu arrêtes de philosopher.

Enfin, ne garde pas activées en dev des features qui multiplient les sources de travail si tu n’en as pas besoin sur le moment. J’ai déjà vu des équipes se battre avec des comportements “aléatoires” qui étaient juste une combinaison de surcouches : instrumentation, analyzers, génération de types, tooling de design system, sur-logging. Le but n’est pas de tout désactiver. Le but est d’être capable de faire une session de dev “light” quand tu chasses un problème mémoire.

Faire un bug report qui aide (et qui évite 40 messages de ping-pong)

Si tu arrives à isoler un souci lié à Next/Turbopack, tu vas peut-être ouvrir un ticket. La différence entre un ticket utile et un ticket ignoré, c’est la qualité de la reproduction. Pas des opinions, pas des captures du moniteur d’activité, mais une recette.

Ce qui aide vraiment, c’est d’indiquer la version exacte de Next, Node, le système, et surtout le mode (Turbopack ou non). Ensuite, donne une reproduction minimale ou un repo réduit, même si ce n’est pas “propre”. Et si tu peux joindre deux heap snapshots (avant/après montée) avec une description de ce que tu as fait entre les deux, tu rends service à tout le monde, toi compris.

Dernier point qui change tout : dis si tu es en monorepo, et comment tes packages internes sont consommés (sources directes, build, symlinks). C’est souvent là que la réalité se cache.

Mon avis (sec) : ajouter de la RAM, c’est parfois OK, mais ce n’est pas un plan

Oui, upgrader une machine peut être rationnel. Les toolchains modernes sont lourdes, et on ne va pas revenir à 2015. Mais si ton Next dev server peut manger “tout ce qu’on lui donne”, tu n’es pas face à un besoin normal. Tu es face à un système qui n’a plus de garde-fous, ou à un graph qui a grossi sans contrôle.

Le bon move, c’est de transformer le problème en données. Tu identifies si c’est le bundler, les watchers, ou ton architecture d’imports. Tu mesures. Tu appliques une mitigation qui a un effet visible. Et seulement après, tu décides si tu as besoin de plus de RAM ou juste de moins de bruit.

Sources

Cet article vous a plu ?

Commentaires

Laisser un commentaire

Entre 10 et 2000 caractères

Les commentaires sont modérés avant publication.

Aucun commentaire pour le moment.

Soyez le premier à donner votre avis !