The error means Puppeteer cannot resolve a browser binary inside the container. Install a compatible browser during the image build and keep its cache available to the runtime user, or point Puppeteer at the real path of a system Chrome/Chromium installation. If the browser is found but will not start, troubleshoot Linux libraries and sandbox permissions separately.
What the error actually means
A successful npm install puppeteer does not guarantee that Chrome exists in the final container. Puppeteer’s package and its browser are separate at deployment time: an install policy can skip the postinstall download, a multi-stage build can discard the browser cache, or the build and runtime users can use different home directories. With puppeteer-core, no browser is downloaded at all; your application must install and select one.
First identify the package and version in your lockfile and in the image:
npm ls puppeteer puppeteer-core
node -p "require('puppeteer/package.json').version" 2>/dev/null || true
node -p "require('puppeteer-core/package.json').version" 2>/dev/null || true
The familiar message “Could not find Chrome (ver. …)” is normally a browser-resolution problem. A later error mentioning process spawning, missing libraries or sandboxing is a launch problem and needs a different fix.
#1 Best Overall
Choose a browser-management strategy
| Strategy | What you install | What your code must do | Best fit |
|---|---|---|---|
| Puppeteer-managed browser | The browser downloaded by Puppeteer, normally Chrome for Testing | Use the matching Puppeteer defaults; keep the cache in the runtime image | Projects wanting Puppeteer’s expected browser version |
| System-managed browser | Chrome or Chromium installed by the Dockerfile or base image | Pass its in-container path with executablePath (or a standard channel) |
Images that own browser updates or use a separately managed browser |
| Official Puppeteer image | Chrome for Testing, dependencies and a pre-installed Puppeteer version | Pin compatible tags and provide the required capability and init process | Teams that prefer a maintained browser-ready base |
Do not mix assumptions. Installing a Debian Chrome package does not make Puppeteer’s downloaded browser appear in ~/.cache/puppeteer, and installing puppeteer-core does not trigger any browser download.
Fix 1: install Puppeteer’s browser in the image build
Allow the install script, or run the documented recovery command
Package-manager settings such as ignored scripts can leave the Node package present while its browser download never runs. After dependencies are installed, explicitly install the browser in the same application directory:
RUN npx puppeteer browsers install
For a Debian/Ubuntu-style custom image, the essential pattern is:
FROM node:22-bookworm-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci
# Required when install scripts were disabled or the download did not run:
RUN npx puppeteer browsers install
COPY . .
CMD ["node", "server.js"]
If your package manager is configured to block lifecycle scripts, either permit Puppeteer’s postinstall script for this build or retain the explicit npx puppeteer browsers install step. Run it after the Puppeteer dependency and its configuration file are available.
Free tools Windows power users keep installed
One-click scans. No signup required.
Keep the browser cache in the final stage
Puppeteer stores downloaded browsers under the installing account’s home directory by default, commonly ~/.cache/puppeteer. In a multi-stage Dockerfile, installing the browser only in a builder stage is ineffective unless that directory is copied into the final stage. A reliable approach is to choose a shared cache path and use it for both build and runtime:
FROM node:22-bookworm-slim AS build
WORKDIR /app
ENV PUPPETEER_CACHE_DIR=/opt/puppeteer-cache
COPY package*.json ./
RUN npm ci
RUN npx puppeteer browsers install
COPY . .
FROM node:22-bookworm-slim
WORKDIR /app
ENV NODE_ENV=production
ENV PUPPETEER_CACHE_DIR=/opt/puppeteer-cache
COPY --from=build /app /app
COPY --from=build /opt/puppeteer-cache /opt/puppeteer-cache
CMD ["node", "server.js"]
Use the same effective home directory, cache variable and Puppeteer configuration at build and runtime. If you switch from root during the image build to a non-root user at runtime, verify that the runtime user can read and execute the copied files.
Verify the install before starting the service
RUN npx puppeteer browsers list
RUN find /opt/puppeteer-cache -maxdepth 4 -type f -name 'chrome' -o -name 'chrome-headless-shell' | head
The exact executable name and directory vary by Puppeteer version. The important checks are that a browser is listed, the directory is in the final image, and the runtime user can access it.
Fix 2: use system Chrome or Chromium explicitly
When your Dockerfile deliberately installs a system browser, locate the binary inside that image instead of guessing a host path:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
which google-chrome || true
which chromium || true
which chromium-browser || true
find /usr/bin /usr/local/bin -maxdepth 2 -type f ( -name 'google-chrome*' -o -name 'chromium*' ) 2>/dev/null
Then pass the discovered path to puppeteer.launch:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
executablePath: process.env.CHROME_PATH || '/usr/bin/google-chrome',
headless: true
});
const page = await browser.newPage();
await page.goto('https://example.com', {waitUntil: 'networkidle2'});
console.log(await page.title());
await browser.close();
})();
Puppeteer’s documented manual-management approach is to call puppeteer.launch with an explicit executablePath, or a channel when the browser is installed in a standard location. The path must exist inside the container, not only on your development machine. Validate that the system browser version is compatible with the Puppeteer version you installed.
Fix 3: use the official Puppeteer Docker image
The official image includes Chrome for Testing, required dependencies and a pre-installed Puppeteer version. Its tags follow Puppeteer versions, so pin the image tag rather than relying on a mutable latest tag when reproducibility matters. Keep your application’s Puppeteer dependency aligned with that image tag.
Rank #3
The Docker guide describes the image as intended for sandboxed browser execution. Running it therefore requires the SYS_ADMIN capability. It also recommends an init process, supplied with Docker’s --init option or a custom entrypoint, so child browser processes are reaped correctly:
docker run --init --cap-add=SYS_ADMIN your-image
Do not add --no-sandbox automatically. Removing the sandbox changes the security model; first provide the capability and user setup expected by the image and your deployment environment.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsSeparate “browser missing” from “browser cannot launch”
Confirm the file exists and is executable
test -x "$CHROME_PATH" && echo "executable" || echo "missing or not executable"
ls -l "$CHROME_PATH"
"$CHROME_PATH" --version
If Puppeteer reports that Chrome cannot be found, fix the path, cache or image contents. If it reports that a process could not spawn, continue with dependency and permission checks.
Find missing shared libraries
Linux can contain the browser file while still failing to start because a shared library is absent. Run the dependency check against the actual binary:
ldd "$CHROME_PATH" | grep not
Install the libraries reported as missing using the package manager for your base distribution, then rebuild the image. Keep this diagnosis separate from Puppeteer’s browser-resolution settings: adding an executablePath cannot repair a missing system library.
Check user, permissions and sandbox context
- Print the effective user and home directory with
idandecho "$HOME". - Ensure the runtime user can read the browser cache and execute the binary.
- Check that a read-only filesystem, restrictive
noexecmount or security profile is not blocking execution. - For the official image, provide
SYS_ADMINand an init process as documented; do not assume local Docker privileges exist in a hosted runner.
Common Docker failure modes and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
Could not find Chrome (ver. …) |
Download skipped or browser cache absent | Run npx puppeteer browsers install after npm ci; include the cache in the final image |
| Works as root during build, fails as app user | Different HOME, cache path or permissions |
Set PUPPETEER_CACHE_DIR consistently and grant read/execute access |
| System Chrome installed but Puppeteer still searches its cache | No explicit browser selection | Set executablePath to the in-container binary or use a supported channel |
spawn, “error while loading shared libraries” or immediate exit |
Browser exists but dependencies are missing | Run ldd chrome | grep not and install the missing libraries |
| Browser starts, then hangs or leaves zombies | No init process managing child processes | Run the container with --init or use an entrypoint that performs equivalent reaping |
Different result after rebuilding with latest |
Mutable image and application versions drifted | Pin a Puppeteer image tag and matching application dependency |
A repeatable diagnostic checklist
- Read
package.jsonand the lockfile; distinguishpuppeteerfrompuppeteer-coreand record the version. - Inspect package-manager policy for ignored lifecycle scripts.
- In the Docker build, run
npx puppeteer browsers installandnpx puppeteer browsers list. - Set and record one cache location, such as
PUPPETEER_CACHE_DIR=/opt/puppeteer-cache, for both build and runtime. - In multi-stage builds, copy that cache into the final stage.
- Run the container as the same user your service uses and verify
HOME, permissions and executable access. - If using system Chrome, discover its path inside the image and pass
executablePath. - If resolution succeeds but launch fails, run
ldd, inspect sandbox capability and add an init process. - Rebuild without stale layers when changing browser-install commands, then test a minimal page navigation before deploying the full workload.
Performance, reliability and image-size considerations
- A Puppeteer-managed browser makes the application’s expected browser version explicit, but the cache increases image-build time and image size.
- A system browser can fit an organization’s patching policy, yet upgrades must be tested against the Puppeteer version and your page behavior.
- Copying a browser cache between stages avoids downloading again, but only if architecture, distribution and runtime user match.
- Pinning both the Node base image and Puppeteer-compatible browser image improves repeatability; rebuilding periodically is still necessary for security updates.
- Run a smoke test in CI that launches the browser, opens a known URL, takes a screenshot and closes cleanly. This catches missing files, libraries and permissions before production.
Or skip the browser setup
If your application only needs reliable website screenshots, ScreenshotNeo provides a hosted API instead of requiring Chrome in your Docker image. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →One GET request returns PNG, JPEG, WebP or PDF:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for all options, including full-page lazy-image loading, CSS-selector element capture, dark mode, device presets, arbitrary viewports, retina scale, PDF paper and page ranges, custom CSS/JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting and the OpenAPI specification. An MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
There is a free allowance of 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it without adding a card.
FAQ
Does installing Chromium with apt automatically fix Puppeteer?
No. Puppeteer may still look for its managed browser. Discover the binary inside the image and provide its path through executablePath, or install Puppeteer’s managed browser instead.
Can I install the browser only when the container starts?
You can, but it makes startup depend on network access and produces less predictable deployments. Installing during the image build gives you a testable, reproducible artifact.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWhy does puppeteer-core behave differently?
puppeteer-core does not download Chrome. Your image and application must install a compatible browser and select it explicitly.
Best Value
- Docker, Docker Swarm, Docker Compose, Programmer, Developer, Coding, Programming, Software Engineer, Code, DevOps, Deploy, Deployment, Kubernetes, Salt, Puppet, Chef, Terraform, Container, AWS, Azure, Cloud, Geek, Funny, Computer, Software, Tech, IT
- Integration, Scrum, Compile, Compilation, Science, Bug, Debug, Python, Linux, Java, Javascript, Scala, Dotnet, Kotlin
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
Is --no-sandbox the universal Docker fix?
No. It is a security trade-off, not a browser-discovery repair. Prefer the sandbox-capable setup and capabilities required by your chosen image; use a reduced-sandbox configuration only after evaluating your deployment’s security requirements.
Frequently Asked Questions
Does installing Chromium with apt automatically fix Puppeteer?
No. Puppeteer may still look for its managed browser. Discover the binary inside the image and provide its path through executablePath, or install Puppeteer’s managed browser instead.
Can I install the browser only when the container starts?
You can, but startup then depends on network access and is less reproducible. Installing during the image build creates a testable deployment artifact.
Why does puppeteer-core behave differently?
puppeteer-core does not download Chrome. Your image and application must install a compatible browser and select it explicitly.
Is –no-sandbox the universal Docker fix?
No. It is a security trade-off, not a browser-discovery repair. Prefer the sandbox-capable setup and required container capabilities.
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.




