Use the smallest isolation boundary that meets your job’s needs. Create several Page objects when tabs can share a session, create a BrowserContext per job when cookies and local storage must be separate, and call puppeteer.launch() multiple times when you need separate browser processes. For a browser managed by another service, use puppeteer.connect() instead of launching one.
Puppeteer’s documentation does not publish a universal maximum for concurrent pages, contexts, or browser processes. Treat concurrency as a capacity-setting exercise for your host and workload: start conservatively, measure CPU, memory, startup time, navigation failures, and queue latency, then adjust.
Choose the right Puppeteer unit
“Multiple Puppeteer instances” can mean three different things. The choice changes session isolation, cleanup behavior, startup cost, and failure scope.
Multiple pages: several tabs in one browser
A Browser can own many Page objects. Pages in the same browser context normally share that context’s cookies, local storage, cache, and other session state. This is the lightest option when jobs are intentionally part of one session, such as opening several product pages after one login.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →#1 Best Overall
Multiple browser contexts: separate sessions in one process
A context is a separate browser session inside one browser process. Cookies and local storage are not shared between contexts, and the context-creation API specifies that contexts do not share cache. This is usually the best default for parallel jobs that must not see one another’s authentication or site state.
The default browser context cannot be closed. Incognito-style contexts created with browser.createBrowserContext() can be closed, and closing one closes the pages it owns.
Multiple browsers: separate processes
Each puppeteer.launch() call starts a distinct browser process. This gives the strongest process boundary and limits some crashes or browser-level configuration conflicts to one group of jobs, but each process has its own startup and resource overhead. Use it when process-level separation is a requirement, not as a substitute for measuring capacity.
Connect to a browser you do not own
puppeteer.connect() attaches to an already running browser through its WebSocket endpoint. Calling browser.disconnect() only detaches Puppeteer; it does not shut down that browser or close its pages. This is useful when a browser pool, container, or another service owns the lifecycle.
Run parallel jobs with isolated browser contexts
The following complete example launches one browser, gives every URL its own context and page, and guarantees cleanup even when navigation fails.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
import puppeteer from 'puppeteer';
const urls = [
'https://example.com',
'https://developer.mozilla.org/',
'https://nodejs.org/'
];
const browser = await puppeteer.launch({
headless: true
});
async function runTask(url) {
const context = await browser.createBrowserContext();
try {
const page = await context.newPage();
await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: 30_000
});
return {
url,
title: await page.title()
};
} finally {
await context.close();
}
}
try {
const results = await Promise.all(urls.map(runTask));
console.log(results);
} finally {
await browser.close();
}
Promise.all() starts every supplied task immediately and rejects when one task rejects. The finally block still closes the browser, while each task’s own finally closes its context and pages.
When pages alone are enough
If sharing cookies and local storage is intentional, create pages directly on one browser (or on one context) instead:
const browser = await puppeteer.launch();
try {
const pages = await Promise.all([
browser.newPage(),
browser.newPage(),
browser.newPage()
]);
await Promise.all(pages.map(async (page, index) => {
await page.goto(urls[index], { waitUntil: 'domcontentloaded' });
console.log(await page.title());
}));
} finally {
await browser.close();
}
Do not use this pattern when one job could log another job out, overwrite shared storage, or expose account-specific data.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Launch several independent browser processes
For process-level separation, launch and close one browser per job (or per worker group). The browser options include browser selection, executablePath, headless mode, userDataDir, startup timeout, and signal handling.
import puppeteer from 'puppeteer';
async function runInOwnBrowser(url) {
const browser = await puppeteer.launch({
headless: true,
timeout: 30_000
});
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });
return await page.title();
} finally {
await browser.close();
}
}
const results = await Promise.all(urls.map(runInOwnBrowser));
console.log(results);
Puppeteer is guaranteed to work with its bundled browser. A custom executablePath is your responsibility: verify that the installed browser version, sandbox settings, and launch flags work together.
Rank #3
Do not confuse parallelism with an unlimited queue
Promise.all(urls.map(...)) is appropriate for a small, known set. For a large queue, use a bounded worker pattern so you can cap active jobs and observe the host. There is no documented Puppeteer concurrency number to copy into every deployment.
async function mapWithWorkers(items, workerCount, worker) {
const output = new Array(items.length);
let next = 0;
async function consume() {
while (true) {
const index = next++;
if (index >= items.length) return;
output[index] = await worker(items[index], index);
}
}
const workers = Array.from(
{ length: Math.min(workerCount, items.length) },
consume
);
await Promise.all(workers);
return output;
}
const results = await mapWithWorkers(urls, 3, runTask);
console.log(results);
The number 3 is an example policy, not a Puppeteer limit. Increase it only after measuring your actual workload. Track resident memory, CPU saturation, browser and context startup time, navigation timeouts, renderer crashes, and job retries. Also account for the target sites’ rate limits and your machine’s file-descriptor and process limits.
Lifecycle rules that prevent leaks
- Close every context in a
finallyblock. Closing a context closes its pages. - Call
browser.close()when your script owns the browser and the work is complete. It gracefully closes the browser and its pages. - Call
browser.disconnect()when you only want to detach from an externally managed browser. The remote browser remains running. - Do not close the default browser context; it cannot be closed.
- Avoid sharing mutable application objects, files, credentials, or result buffers between concurrent tasks unless that sharing is deliberate and synchronized.
Configure each job deliberately
Navigation and readiness
Choose a navigation timeout and a readiness condition that match the site. domcontentloaded returns earlier than waiting for every resource; applications that render after hydration may need a selector wait or an explicit delay. Always handle timeouts and decide whether a retry is safe.
Session and identity
Use one context per identity when authentication must be isolated. Keep credentials out of shared globals, and never reuse a persistent userDataDir concurrently from multiple browser processes.
Headless and executable settings
Set headless mode explicitly for predictable deployments. If you select a system browser with executablePath, test it on the same operating system and container image used in production; Puppeteer’s bundled browser is the supported baseline.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Common failures and fixes
“Browser was not found” or launch fails
Use the bundled browser or verify the configured executable path exists and is executable. Check the startup timeout and inspect the process’s stderr. In containers, confirm required libraries and sandbox permissions are present.
Jobs unexpectedly share login state
You created several pages in one context. Create a new context for each isolated job, then close it after the job finishes.
Memory usage keeps growing
Look for pages or contexts that are not closed after errors. Replace an unbounded Promise.all with a worker queue, and measure whether one browser with contexts or several browser processes is more stable for your workload.
One failure cancels the whole batch
Promise.all rejects on the first rejection. Catch errors inside each worker and return a structured success/error result when the remaining jobs should continue.
Disconnecting did not stop Chrome
That is expected: disconnect() detaches only Puppeteer. Use browser.close() if your code owns the browser, or stop it through the service that launched it.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallBest Value
Pages hang or time out under load
Reduce active workers, set explicit navigation and operation timeouts, and inspect CPU, memory, network saturation, and the destination’s response behavior. There is no official universal “safe” concurrency value.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Which design should you use?
| Design | Isolation | Lifecycle | Best fit |
|---|---|---|---|
| Several pages in one context | Shared session storage | Close pages or the owning browser | Tabs that intentionally share login and state |
| One context per job | Separate cookies, local storage, and cache | Close each context; then close the browser | Most parallel, independently authenticated jobs |
| One browser per job or worker | Separate browser processes | Close each browser process | Strong process boundaries or incompatible browser settings |
puppeteer.connect() |
Defined by the remote browser service | Disconnect without shutting down the remote browser | Externally managed browser pools |
Or skip the browser setup
If your goal is reliable website screenshots rather than browser automation itself, ScreenshotNeo provides a single HTTP request and an MCP server for AI clients such as Claude and Cursor. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; those cleanup steps can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
See the complete parameter reference in the ScreenshotNeo documentation. The same endpoint can return PNG, JPEG, WebP, or PDF.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
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)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also supports full-page captures with lazy images, CSS-selector element shots, dark mode, device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, pre-capture clicks, hidden selectors, selector/delay/network-idle waits, request and resource blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification.
Recommended Free Tools
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account to try it.
Frequently Asked Questions
Can I share one Puppeteer browser across worker threads?
Share work through an explicit queue and give each job a page or context; do not assume arbitrary mutable objects are safe to share. The browser’s documented isolation boundary is the browser context.
Is a browser context the same as a separate Chrome process?
No. Contexts are separate sessions inside one browser process. Separate processes require separate launches or externally managed browser instances.
What should I measure before increasing concurrency?
Measure memory, CPU, startup and navigation latency, timeout and crash rates, queue delay, and destination-site responses on the exact host and workload you will operate.
Free tools Windows power users keep installed
One-click scans. No signup required.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




