Get the Puppeteer Browser Process
Use Puppeteer’s Browser object to access its child process, understand launch versus connect, and clean up browser sessions safely.
To get the Node.js child process for a browser launched by Puppeteer, call browser.process() on the Browser object returned by puppeteer.launch(). The returned value is a Node.js ChildProcess (or null when Puppeteer does not have an associated locally launched process). Keep the browser handle for browser operations, and use the process handle only when you specifically need operating-system process details or signaling.
Puppeteer Browser API documents the process method and lifecycle methods. A browser created with puppeteer.connect() is a connection to a browser that may be owned elsewhere; do not assume Puppeteer owns or can return that remote machine’s operating-system process.
Get the process from a launched browser
Install Puppeteer in a Node.js project, then launch the browser and inspect its process:
npm install puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const child = browser.process();
if (child === null) {
console.log('Puppeteer has no local child process for this browser.');
} else {
console.log({
pid: child.pid,
executable: child.spawnfile,
args: child.spawnargs,
exitCode: child.exitCode,
signalCode: child.signalCode,
});
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
console.log(await page.title());
}
} finally {
await browser.close();
}
Save it as browser-process.mjs and run node browser-process.mjs. With CommonJS, replace the import with const puppeteer = require('puppeteer'); and run the file in a CommonJS project.
browser.process() is a getter for the associated child process; it does not start the browser. Call it after a successful launch. The pid can be useful for diagnostics, while spawnfile and spawnargs show what Node spawned. Avoid logging arguments or environment values indiscriminately in production because launch configuration can contain sensitive details.
Launch, connect, close, and disconnect
There are two distinct ways to obtain a Puppeteer Browser handle:
| Approach | Use it when | Process ownership and cleanup |
|---|---|---|
puppeteer.launch() |
Your Node.js program starts Chrome or Chromium. | Puppeteer manages the browser process. browser.process() provides its associated child process. browser.close() closes the browser and its pages. |
puppeteer.connect() |
A browser is already running, often in a separate service or container, and exposes a WebSocket endpoint. | Your program attaches to it. browser.disconnect() detaches Puppeteer while leaving the browser running. The external owner is responsible for stopping it. |
Launch a browser and shut it down when your work is complete:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
// Read or capture the page here.
} finally {
await browser.close();
}
Connect to an existing browser when you have its WebSocket endpoint:
import puppeteer from 'puppeteer';
const browserWSEndpoint = process.env.PUPPETEER_WS_ENDPOINT;
if (!browserWSEndpoint) throw new Error('Set PUPPETEER_WS_ENDPOINT');
const browser = await puppeteer.connect({ browserWSEndpoint });
try {
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
} finally {
// Detach this client. The browser process remains running.
browser.disconnect();
}
The endpoint is sensitive access information. Pass it through a secret manager or environment variable, and avoid placing it in source control or logs. Puppeteer’s browser management guide describes launching and connecting by WebSocket endpoint, including the difference between close and disconnect.
Choose the browser executable and launch options
By default, Puppeteer downloads and uses a browser version selected for that Puppeteer release. This bundled browser is the compatibility path Puppeteer guarantees. If you intentionally need a separately installed Chrome or Chromium, set executablePath and check the version pairing yourself:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
executablePath: process.env.CHROME_PATH,
headless: true,
args: ['--no-sandbox'],
timeout: 30_000,
});
try {
const child = browser.process();
console.log(child?.pid ?? 'No locally associated child process');
} finally {
await browser.close();
}
Only add flags such as --no-sandbox when your deployment environment requires them and you understand the security implications. Do not copy a launch argument list from another environment without checking why each flag is needed.
Relevant launch controls include:
executablePath: path to a Chrome or Chromium binary. The binary must exist and be compatible with the Puppeteer version; only the bundled browser is guaranteed to work.args: additional command-line arguments passed to the browser. Use these for a deliberate browser configuration, not as a general cure for launch failures.env: environment variables for the child process. Be mindful of secrets and avoid dumping the full environment into logs.timeout: maximum startup wait in milliseconds. The current launch options reference documents a 30-second default and0to disable the timeout.handleSIGINT,handleSIGTERM, andhandleSIGHUP: control Puppeteer’s signal handling for browser shutdown. Review the behavior before combining Puppeteer with your own process-level shutdown handlers.headless: choose headless operation or a visible browser where the environment supports it.
Consult the current LaunchOptions reference for the full option set and exact types for your installed version. The configuration guide covers Puppeteer configuration and browser installation.
Use the child process carefully
The child process handle is useful for observation and, in special cases, process-level integration. Most browser work should use Puppeteer’s Browser, Page, and target APIs rather than operating-system signals.
- Check for
null. A process handle may be unavailable, especially when the browser was connected to rather than launched locally. Do not dereferencechild.pidwithout checking. - Prefer
browser.close()for normal cleanup. It closes the browser and associated pages through Puppeteer. Callingchild.kill()bypasses that browser-level cleanup and can leave work incomplete. - Do not confuse a PID with ownership. A PID is local to the operating system or container where the process runs. A remote browser’s PID is not available through a client connected from another machine.
- Expect the process to exit. A child can terminate because of a crash, an external signal, or a container shutdown. Listen for child-process exit events if your application needs diagnostics, and handle browser disconnection in the Puppeteer code.
- Use one lifecycle owner. In a service that shares a browser, define which component launches and closes it. A worker that only connected should generally disconnect, not close a browser it does not own.
- Keep browser count intentional. Launching a browser per request adds startup work and resource use. Reuse a managed browser when appropriate, while isolating pages and ensuring cleanup for each task.
Performance, reliability, and cost
Launching Chrome has startup latency and consumes memory and CPU. Reusing a browser can reduce repeated startup work, but it creates a long-lived process that needs health handling, limits, and orderly shutdown. Track whether the browser is connected, close pages created for completed jobs, and restart a browser through its owner when it becomes unhealthy.
Set a startup timeout that matches the deployment environment, and treat a timeout as a launch failure rather than assuming a process handle was created. For connected browsers, also handle an unavailable or stale WebSocket endpoint. Keep browser instances bounded under concurrency; unbounded launch-per-task patterns can exhaust memory or process limits.
There is no universal process-level performance or cost figure: resource use depends on the page, browser version, concurrency, and runtime. Budget for compute and memory in your own environment, and use the bundled browser unless there is a concrete reason to manage a different executable.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
browser.process() is null |
The browser connection does not have a locally owned child process, or the current mode does not expose one. | Check whether you used launch() or connect(). For a connected browser, inspect and manage the process in the service or container that launched it. |
| Launch reports that the executable cannot be found | executablePath is wrong, unset, or points to a binary absent from the runtime. |
Check the resolved path and file availability in the same container or host running Node.js. Remove the override to use Puppeteer’s bundled browser if that is suitable. |
| Browser starts locally but fails in deployment | The runtime may lack operating-system libraries or other dependencies required by headless Chrome. | Follow the Puppeteer troubleshooting instructions for your actual operating system and container. Puppeteer specifically notes that the default Google Cloud Run Node.js runtime lacks required system packages and advises supplying a Dockerfile with them; this is a Cloud Run-specific note, not a universal runtime fix. |
| Browser startup times out | Startup takes longer than the configured timeout, the executable cannot initialize, or the environment is resource constrained. | Check the executable and runtime dependencies first, then inspect resource limits and logs. Increase timeout only if slower startup is expected; setting it to 0 disables the startup timeout. |
connect() cannot reach the browser |
The WebSocket endpoint is wrong, unavailable, blocked by networking, or belongs to a browser that has exited. | Verify endpoint configuration and network access from the Node.js runtime, and confirm the external browser owner reports the instance as running. |
| Browser remains running after the script finishes | The code disconnected rather than closed, or its cleanup path did not run. | For a browser launched by this program, ensure a finally block calls await browser.close(). For a connected browser, ask its owner to stop it if shutdown is intended. |
| Process disappears unexpectedly | The browser crashed, received a signal, or the host/container terminated it. | Observe child-process exit information when available, capture Puppeteer errors, and check runtime logs and resource limits. Recover by launching a new browser or reconnecting to a healthy externally managed one. |
Use Puppeteer’s troubleshooting guide for platform-specific dependencies and current deployment guidance.
Or skip the browser setup
If your goal is a page screenshot rather than managing Chrome’s process, ScreenshotNeo provides a screenshot API and MCP server. A single GET request can return a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
ScreenshotNeo removes cookie banners, popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.
FAQ
Does browser.process() launch Chrome?
No. It returns the process associated with a browser handle when available. Start a local browser with puppeteer.launch().
Can I get the remote browser’s PID after puppeteer.connect()?
A connection gives Puppeteer control of browser targets, not automatic access to the operating system process on the remote host. Query that host through its own process-management system if you need its PID.
Should I use close() or disconnect()?
Use close() to shut down a browser your program launched. Use disconnect() to detach from an externally managed browser that should keep running.
Is the Chrome PID stable?
No. Process identifiers are runtime-specific and can change each time a browser starts. Treat them as temporary diagnostics, not persistent browser identity.
Can Puppeteer work with system Chrome?
Yes, with an appropriate executablePath, but Puppeteer only guarantees compatibility with its bundled browser. Check the current configuration guide before pinning a separate executable.


