2,104 orphaned headless Chromes: a memory leak with Puppeteer
The automation container was about to run out of memory. n8n was only using 264 MB. The rest was 2,104 headless Chrome processes piled up over 22 days.
- JavaScript / TypeScript
- Docker
- Linux (Debian, Alpine, Arch)
Symptom
Monitoring warned the n8n container had 31 GB used out of 32 and 154 MB free. The n8n process was using 264 MB.
ps aux --sort=-%mem | head
# → 2,104 …/chrome-linux64/chrome processes, the oldest from 22 days ago
Cause
A small Puppeteer API opens a headless browser for the n8n flows and reuses it. In its routes' catch blocks, on any error (a timeout, a selector that never shows up), the code did browser = null without calling browser.close().
That drops the JavaScript reference, but the system's Chrome process lives on forever. Every failure of the source site left one more browser behind.
// before
} catch (e) { browser = null; throw e; }
// after
async function closeBrowser() {
if (browser) await browser.close().catch(() => {});
browser = null;
}
} catch (e) { await closeBrowser(); throw e; }
Result
- Killing the orphans freed about 28 GB at once.
- Since the fix, even when the source site fails again and again, the Chrome process count stays at zero.
- The API file is mounted as a volume: it could be fixed with a container restart, without rebuilding the image.