Install puppeteer when you want Puppeteer to download a compatible Chrome for Testing build, or install puppeteer-core when you will manage Chrome yourself. The Node API is puppeteer.launch(options). A normal installation is npm i puppeteer; if the browser download was skipped, run npx puppeteer browsers install. With puppeteer-core, always provide executablePath or channel.
Choose how Puppeteer will obtain Chrome
Puppeteer is a JavaScript library that controls Chrome or Firefox through Chrome DevTools Protocol or WebDriver BiDi. The npm package and the browser binary are separate concerns, so decide who owns the browser before writing launch code.
| Strategy | Install | Browser ownership | Launch requirement | Best use |
|---|---|---|---|---|
| Bundled Puppeteer | npm i puppeteer |
Puppeteer downloads Chrome for Testing | Usually no path is needed | Local development and predictable version matching |
| Managed browser | npm i puppeteer-core |
You provide Chrome, Chromium or a remote browser | executablePath or channel |
System Chrome, custom containers and controlled infrastructure |
| Manual Puppeteer browser install | Puppeteer package plus npx puppeteer browsers install |
Puppeteer’s cache | Use Puppeteer’s resolved executable | CI or package managers that suppress post-install scripts |
The bundled route is normally the least surprising. Puppeteer’s installer downloads a recent Chrome for Testing build and a chrome-headless-shell binary. The download is large—approximately 170 MB on macOS, 282 MB on Linux and 280 MB on Windows—so account for it in build and cache storage.
Install Puppeteer and run your first headless script
- Make a project and initialize npm:
mkdir headless-demo cd headless-demo npm init -y - Install the batteries-included package:
npm i puppeteer - Create
capture.mjswith this complete example:import puppeteer from 'puppeteer'; const browser = await puppeteer.launch({headless: true}); try { const page = await browser.newPage(); await page.goto('https://example.com', {waitUntil: 'networkidle2'}); console.log(await page.title()); } finally { await browser.close(); } - Run it:
node capture.mjs
puppeteer.launch() returns a Promise for a Browser. The browser runs headless by default; setting headless: true makes that intent explicit. Always close the browser in a finally block so failed navigation does not leave Chrome processes behind.
#1 Best Overall
Install when package scripts are blocked
npm, pnpm, Yarn Berry, Bun and Deno policies can disable install scripts. In that case, the JavaScript package may be present while its browser is missing. Install the package, then explicitly download the browser:
npm i puppeteer
npx puppeteer browsers install
Run the second command in the same build stage and user account that will execute your application. In CI, persist the resulting browser cache between jobs or image layers; otherwise every clean build downloads Chrome again.
Use puppeteer-core with a system or managed Chrome
puppeteer-core contains the library only. It does not download Chrome, and its launch API requires either an executable path or a browser channel.
npm i puppeteer-core
import puppeteer from 'puppeteer-core';
const browser = await puppeteer.launch({
executablePath: process.env.CHROME_BIN
// Alternatively: channel: 'chrome'
});
try {
const page = await browser.newPage();
await page.goto('https://example.com', {waitUntil: 'networkidle2'});
console.log(await page.title());
} finally {
await browser.close();
}
Set CHROME_BIN to a path that exists inside the runtime, not merely on your development machine. A channel such as chrome asks Puppeteer to locate an installed branded Chrome. Puppeteer works best with the Chrome for Testing version it downloads and does not guarantee compatibility with arbitrary browser versions, so test managed upgrades before deploying them.
Free tools Windows power users keep installed
One-click scans. No signup required.
Understand the browser cache and fix “Could not find Chrome”
Since Puppeteer v19.0.0, downloaded browsers are cached under ~/.cache/puppeteer by default. A cache that exists on a laptop may not exist in a container, CI worker or serverless build.
Rank #2
- Check whether your package manager blocked the install script.
- Run
npx puppeteer browsers installexplicitly. - Confirm the runtime user can read the cache and execute the binary.
- Make the cache survive the build boundary. If a platform preserves
node_modulesbut skips post-install hooks, configure the Puppeteer cache undernode_modules/.puppeteer_cacheas recommended for Google runtimes. - If you use a system browser, stop looking in Puppeteer’s cache and verify
CHROME_BIN,executablePathorchannelinstead.
A common failure pattern is “works locally, fails in CI”: local installation ran the download hook, while the CI image copied only JavaScript dependencies. Make browser installation and cache persistence explicit in the image or pipeline.
Launch options that matter in production
Headless mode
Use headless: true for normal automation. A visible browser is useful while debugging locally, but requires a display server in Linux environments. Keep the production setting explicit so a dependency upgrade cannot silently change behavior.
Executable selection
Use executablePath for an exact binary path and channel when Chrome is installed in a known browser channel. Do not set both unless you have a deliberate reason; an explicit path removes discovery ambiguity.
Arguments and the sandbox
Chrome’s sandbox is a host-protection layer. Run as a non-root user with a real home directory whenever possible. The --no-sandbox argument is an environment-specific exception for content you absolutely trust, not a default fix. Disabling the sandbox reduces isolation and can turn a browser compromise into a host compromise.
Navigation readiness
page.goto() can wait for a lifecycle condition such as networkidle2, but pages that poll, stream or keep analytics connections open may never become truly idle. For deterministic jobs, wait for a specific selector or application signal after navigation and use a bounded timeout. Treat a timeout as a diagnosable result, not proof that the URL is permanently unavailable.
Rank #3
Profiles and parallelism
Give independent jobs separate temporary profile directories. Reusing one profile across concurrent Chrome processes can corrupt locks and cookies. Reuse a browser for several pages when startup cost dominates, but isolate tenants and close pages after each job to cap memory.
Linux and Docker: dependencies, users and writable paths
Headless Chrome is not a single portable file. Linux launches require shared libraries, permissions and writable profile and cache directories.
Diagnose missing shared libraries
On Debian-family systems, inspect the downloaded Chrome binary:
ldd /path/to/chrome | grep not
Install the missing packages. Common requirements include libnss3, libgbm1, libgtk-3-0, libasound2, libx11-6 and libx11-xcb1. The exact package names can vary by distribution release, so use your image’s package manager and verify the result with ldd.
Use a non-root container user
- Create a user with a writable home directory.
- Give that user ownership of the Puppeteer cache, temporary profile and application directory.
- Ensure the container has enough shared memory and temporary disk for the pages you capture.
- Install the browser and dependencies in the image layer that runs the application.
Running as root often produces sandbox errors and permission surprises. Adding --no-sandbox may hide the symptom but should be reserved for trusted content and a deliberately isolated environment.
Rank #4
Alpine Linux
Chrome does not support Alpine out of the box. If you choose Alpine, match its Chromium package to your Puppeteer version and test the complete image, including fonts, shared libraries and sandbox behavior. A Debian-family base image is usually simpler when you need Puppeteer’s downloaded Chrome for Testing build.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Cloud Run, App Engine and Functions
Google Cloud Run
The default Node.js runtime does not include the system packages required by Headless Chrome. Build a custom Docker image that installs Chrome or Chromium, all required libraries, a non-root user and a persistent-enough Puppeteer cache for the image lifecycle. Set executablePath when the image owns the browser, and test the exact deployed image rather than only the local source tree.
Google App Engine standard and Cloud Functions
The documented runtimes include the system packages needed by Puppeteer. Install dependencies during the build and keep the Puppeteer cache in a build-persistent location when install hooks may not run again. Cold starts still pay browser startup and page-loading costs, so avoid downloading a browser on every invocation.
Serverless limits
Bound navigation and total job time, close every browser or page, and log whether failure occurred during browser launch, navigation or page evaluation. Keep concurrency within the memory available to each instance; several Chromium processes can exhaust a small function quickly.
Troubleshoot the failure you actually have
| Symptom | Likely cause | Fix |
|---|---|---|
| Could not find Chrome | Install script was skipped or cache is absent | Run npx puppeteer browsers install, persist ~/.cache/puppeteer, or set a verified executablePath/channel. |
| Failed to launch: missing shared library | Linux image lacks Chrome dependencies | Run ldd chrome | grep not and install packages such as libnss3, libgbm1 and libgtk-3-0. |
| Sandbox or permission error | Running as root or using unwritable home/profile paths | Use a non-root user and writable directories; use --no-sandbox only for trusted content in an isolated environment. |
| Navigation timeout | Slow site, blocked request, or a page that never becomes idle | Use a bounded timeout, wait for a concrete selector, and capture console/request logs to identify the stalled resource. |
| Works locally but not in Docker | Different OS libraries, user, cache or environment variables | Reproduce with the deployed image, inspect the binary with ldd, verify ownership and print the resolved executable path. |
| Alpine launch failure | Chrome/Chromium is not supported by the base image without extra work | Use a compatible Chromium package and Puppeteer version, or switch to a Debian-family image. |
| Cloud Run launch failure | Default runtime lacks Headless Chrome packages | Deploy a custom image containing the browser, dependencies, writable paths and a non-root user. |
Performance, reliability and cost planning
- Startup: launching Chrome is expensive compared with opening a new page. Reuse one browser for a controlled batch, but isolate jobs that handle different users or credentials.
- Downloads: cache the approximately 170–282 MB browser artifact in CI and container layers instead of downloading it for every build.
- Memory: cap parallel pages and close them promptly. Full-page layouts, large images and multiple browser processes increase resident memory.
- Determinism: pin your Puppeteer dependency and test the browser version shipped with it. Arbitrary system Chrome versions have no compatibility guarantee.
- Security: keep the Chrome sandbox enabled, avoid untrusted extensions and never place secrets in URLs or page content.
- Observability: record launch mode, executable path, browser version, navigation URL, timeout stage and final error. This separates missing binaries from site-level failures.
Or skip the browser setup: ScreenshotNeo
If your goal is a clean website screenshot rather than browser automation logic, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and the response reports the result in X-Page-Verdict and X-Billed headers.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsSee the ScreenshotNeo API documentation for all options. This cURL request returns a WebP image:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same endpoint from 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)
And 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 offers an MCP server for AI clients such as Claude and Cursor, with take_screenshot, get_page_info and capture_pdf tools. Its 63 options include full-page captures with lazy images loaded, CSS-selector element shots, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, pre-capture clicks, selector/delay/network-idle waits, ad and tracker blocking, custom headers/cookies/user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of 100 URLs per call, a usage API and an OpenAPI specification. Common parameter names from other screenshot APIs are accepted to ease migration.
| Plan | Included shots | Price |
|---|---|---|
| Free | 1,000 per month | $0, no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
Yearly billing gives two months free, and every feature is available on every plan. Sign up for 1,000 free screenshots a month with no card.
FAQ
Can Puppeteer drive Firefox?
Yes. Puppeteer’s high-level API supports Chrome and Firefox through DevTools Protocol or WebDriver BiDi, although this installation guide focuses on Chrome binaries and Linux deployment requirements.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Why does a successful HTTP response still produce no screenshot?
HTTP success only proves that a server answered. Browser rendering can still fail because of scripts, blocked resources, missing libraries, sandbox restrictions or a page that never reaches your chosen readiness condition.
Should I commit the downloaded Chrome binary to Git?
No. Keep the binary in a build cache or image layer, install it reproducibly with Puppeteer’s browser command, and record the dependency version used to create that cache.
Frequently Asked Questions
Can Puppeteer drive Firefox?
Yes. Puppeteer’s high-level API supports Chrome and Firefox through DevTools Protocol or WebDriver BiDi, although this guide focuses on Chrome binaries and Linux deployment.
Why does a successful HTTP response still produce no screenshot?
An HTTP response only proves that a server answered; browser rendering can still fail because of scripts, blocked resources, missing libraries, sandbox restrictions or an unmet readiness condition.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Should I commit the downloaded Chrome binary to Git?
No. Keep it in a reproducible build cache or image layer and record the Puppeteer dependency version used to create that cache.
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.

