2.104 Chrome sin cabeza huérfanos: una fuga de memoria con Puppeteer
El contenedor de las automatizaciones estaba a punto de quedarse sin memoria. n8n solo usaba 264 MB. Lo demás eran 2.104 procesos de Chrome sin cabeza acumulados durante 22 días.
- JavaScript / TypeScript
- Docker
- Linux (Debian, Alpine, Arch)
Síntoma
La monitorización avisó de que el contenedor de n8n tenía 31 GB usados de 32 y 154 MB libres. El proceso de n8n usaba 264 MB.
ps aux --sort=-%mem | head
# → 2.104 procesos …/chrome-linux64/chrome, el más antiguo de hacía 22 días
Causa
Una pequeña API con Puppeteer abre un navegador sin cabeza para los flujos de n8n y lo reutiliza. En los catch de sus rutas, ante cualquier error (un timeout, un selector que no aparece), el código hacía browser = null sin llamar a browser.close().
Eso descarta la referencia de JavaScript, pero el proceso de Chrome del sistema sigue vivo para siempre. Cada fallo de la web de origen dejaba un navegador más.
// antes
} catch (e) { browser = null; throw e; }
// después
async function closeBrowser() {
if (browser) await browser.close().catch(() => {});
browser = null;
}
} catch (e) { await closeBrowser(); throw e; }
Resultado
- Matar los huérfanos liberó unos 28 GB al instante.
- Desde el arreglo, aunque la web de origen falle una y otra vez, el recuento de procesos de Chrome se queda en cero.
- El fichero de la API está montado como volumen: se pudo arreglar con un reinicio del contenedor, sin reconstruir la imagen.