Puppeteer Browser Launch Options Explained
Learn how Puppeteer launch options control browser selection, headless mode, arguments, startup, and debugging—with runnable examples and fixes for common errors.
Puppeteer browser launch options are the settings passed to puppeteer.launch() to choose and configure a browser process. The defaults work for many scripts: Puppeteer launches its bundled Chrome for Testing in new headless mode, waits up to 30 seconds for startup, and opens an initial page. Change an option when you need a different browser binary, rendering mode, startup behavior, debugging output, or process lifecycle.
This guide follows the Puppeteer 25.12.0 API. Option names, defaults, and browser compatibility can change, so check the current LaunchOptions reference when upgrading.
1. A runnable launch-options example
Install Puppeteer, which includes a compatible browser, then save this as launch.js and run node launch.js:
npm install puppeteer
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
timeout: 30_000,
defaultViewport: { width: 1440, height: 900 },
args: ['--disable-dev-shm-usage'],
dumpio: false,
});
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
console.log(await page.title());
} finally {
await browser.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
The extra argument here is an example of an environment-specific adjustment, not a universal requirement. Start with the defaults and add flags only to solve a known issue. defaultViewport is inherited from the connection options and defaults to 800 × 600; setting it at launch makes newly created pages use the chosen dimensions.
2. Browser selection and executable path
browser selects the browser type and defaults to 'chrome'. With the full puppeteer package, Puppeteer normally uses its bundled Chrome for Testing. This is the recommended route because Puppeteer says it works best with that browser and does not guarantee operation with other Chrome versions.
With puppeteer-core, provide either executablePath or channel. The launch API states: “When using with puppeteer-core, options.executablePath or options.channel must be provided.” See the launch() API.
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.launch({
browser: 'chrome',
executablePath: '/usr/bin/google-chrome',
headless: true,
});
try {
console.log(await browser.version());
} finally {
await browser.close();
}
})();
Use channel when you want an installed Chrome release channel, for example a channel supported by your platform. Use executablePath when you know the exact binary path. The API recommends setting browser alongside executablePath, since the browser otherwise defaults to Chrome.
| Choice | Use it when | Tradeoff |
|---|---|---|
Bundled browser with puppeteer |
You want Puppeteer’s expected browser setup | Browser download adds install size and setup time |
channel |
You need an installed Chrome channel | Available channels and binaries depend on the host |
executablePath |
You need a specific installed binary | You own version compatibility and path management |
3. Headless mode, DevTools, and rendering
headless defaults to true, which selects new headless mode. Set it to 'shell' for the old headless shell mode. Set it to false for headful Chrome. Enabling devtools: true forces headful mode, even if you also set headless: true.
// New headless mode (default)
const browser = await puppeteer.launch({ headless: true });
// Old headless shell mode
const shell = await puppeteer.launch({ headless: 'shell' });
// Visible browser; DevTools forces headful mode
const debug = await puppeteer.launch({ headless: false, devtools: true });
Headful mode is useful for observing interactions and diagnosing differences that are hard to understand from logs. It requires a display environment unless your operating system or container provides a virtual display. For routine server capture, headless mode avoids that display dependency.
4. Command-line arguments and Puppeteer defaults
args adds command-line flags to the browser process. Puppeteer also supplies its own default arguments. Use puppeteer.defaultArgs() to inspect the defaults for your installed version.
const defaults = puppeteer.defaultArgs();
console.log(defaults);
const browser = await puppeteer.launch({
args: ['--window-size=1440,900'],
});
ignoreDefaultArgs has two forms:
trueomits all Puppeteer default arguments.- An array filters only the named default arguments, allowing the rest to remain.
// Filter a specific default only when you know why it must be removed.
const browser = await puppeteer.launch({
ignoreDefaultArgs: ['--some-specific-flag'],
});
Removing all defaults can break assumptions Puppeteer makes about browser startup and automation. Prefer adding a flag with args, or filtering one known flag, rather than setting ignoreDefaultArgs: true. The defaultArgs() API and LaunchOptions reference document these controls.
5. Profiles, extensions, environment, and logging
Profile directory
userDataDir sets the browser profile directory. Use a separate directory for each concurrently running browser process; sharing a profile between processes can cause locking and state conflicts. A persistent profile can retain cookies and other browser data between runs, so use a temporary or isolated profile when reproducibility matters.
Extensions
enableExtensions can prevent Puppeteer from supplying default arguments that disable extensions, or accept paths to unpacked extensions. extensionsEnabledInIncognito names extensions to enable in off-the-record profiles. These controls are browser-specific in their practical effect; check the API for supported behavior.
Environment and browser output
env controls the environment variables visible to the browser and defaults to process.env. If you provide a custom environment object, include any variables the browser needs. dumpio: true forwards browser stdout and stderr to the Node.js process, which is useful when the browser fails before a page can be inspected.
const browser = await puppeteer.launch({
env: { ...process.env, LANG: 'en_US.UTF-8' },
dumpio: true,
});
6. Startup lifecycle, signals, and transport
| Option | Documented behavior | When to adjust it |
|---|---|---|
timeout |
Startup timeout in milliseconds; default 30,000. Set to 0 to disable. |
Increase it for slow or heavily loaded hosts; disabling it means your own process must handle a launch that never completes. |
waitForInitialPage |
Defaults to true. |
Set false for startup cases such as Chrome’s --no-startup-window. |
handleSIGHUP, handleSIGINT, handleSIGTERM |
Signal handlers default to true. | Disable only if your application owns shutdown handling. |
signal |
An AbortSignal; aborting it closes the browser. | Connect browser lifetime to a task cancellation controller. |
pipe |
Uses a pipe instead of WebSocket; supported only for Chrome. | Choose only when your environment benefits from pipe transport. |
Launch options extend ConnectOptions. In addition to defaultViewport, the inherited protocolTimeout sets the timeout for an individual protocol call and defaults to 180,000 ms. This is separate from the overall browser startup timeout. See the ConnectOptions reference.
const controller = new AbortController();
const browser = await puppeteer.launch({
signal: controller.signal,
timeout: 45_000,
protocolTimeout: 180_000,
});
// Later, when the job is cancelled:
controller.abort();
7. Configuration files and environment overrides
Some defaults can be configured outside the individual launch call. Puppeteer’s configuration documentation identifies PUPPETEER_BROWSER and PUPPETEER_EXECUTABLE_PATH as environment-variable overrides. Configuration is useful when multiple scripts share a browser setup; explicit launch options are easier to see when behavior is specific to one job.
When troubleshooting precedence or a browser mismatch, inspect both the configuration file and the process environment, then log the effective executable path and browser version. Refer to the Puppeteer configuration guide.
8. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
An executablePath or channel must be specified |
puppeteer-core was launched without a browser binary choice. |
Pass a valid executablePath or channel, or install and use the full puppeteer package. |
| Browser executable not found | The configured path is wrong, the browser is not installed, or the runtime user cannot access it. | Check the path inside the actual container or host; confirm the binary exists and is executable. |
| Launch times out | Startup exceeds the 30-second default, dependencies are missing, or the process cannot start. | Enable dumpio, check host/container browser requirements, and raise timeout if startup is simply slow. |
| Browser starts but automation behaves unexpectedly | Defaults were broadly removed or conflicting flags were added. | Remove experimental flags, restore defaults, and add one change at a time. |
| Works locally but fails in deployment | Different executable, environment, permissions, or display setup. | Log browser.version(), inspect environment and executable path, and use headless mode where no display exists. |
| Concurrent jobs collide or profile is locked | Processes share a userDataDir. |
Allocate a distinct profile directory per process or avoid persistent profiles. |
| A protocol operation times out after launch | The per-call protocolTimeout is reached; this is distinct from launch timeout. |
Inspect the slow operation and page state, then adjust protocolTimeout if the operation legitimately needs more time. |
9. Performance, reliability, and cost
Browser launch is a process startup, so reusing one browser for a batch of related pages usually avoids repeated startup overhead. Reuse has a lifecycle cost: close each page when finished, close the browser when the batch ends, and isolate profiles where state must not leak. If a browser becomes unhealthy, restart it at a controlled boundary rather than retrying every page indefinitely.
Keep default arguments unless a measured operational need justifies changing them. Set startup and protocol timeouts according to separate failure modes. Capture browser stderr with dumpio during diagnosis, then choose an appropriate logging policy for normal operation. An external binary can simplify control over installed software but makes compatibility and updates your responsibility; the bundled browser is the more predictable starting point.
Local Puppeteer has no per-screenshot service charge, but it consumes compute, memory, storage, and engineering time to install, run, monitor, and update a browser. The right comparison is total operating cost: browser infrastructure and maintenance versus a hosted API charge, based on capture volume and required controls.
10. Or skip the browser setup
For a screenshot without managing a local browser process, ScreenshotNeo accepts one GET request with a URL and returns PNG, JPEG, WebP, or PDF. Its API documentation is at ScreenshotNeo docs.
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}`);
ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
11. FAQ
Does headless: true mean the old headless browser?
No. In the current API it selects new headless mode. Use 'shell' for the old headless shell.
Can I use a custom Chrome version with Puppeteer?
You can point to an installed browser with channel or executablePath, but Puppeteer does not guarantee operation with Chrome versions other than its bundled Chrome for Testing.
What is the difference between timeout and protocolTimeout?
timeout limits browser startup. protocolTimeout limits an individual protocol call after connection.
Should I set ignoreDefaultArgs: true?
Usually no. Puppeteer cautions that callers likely need its default arguments. Filter a specific argument only for a clear, diagnosed reason.


