The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Most Browsershot failures on Laravel Sail come from one boundary: PHP runs inside the Sail application container, while Node.js, Puppeteer, Chrome, their downloaded browser cache, and Linux libraries must also be available and usable inside that same container. A Chrome installation on your host does not automatically exist in Sail.
Fix the chain in order: identify the container and runtime user, verify Node and npm, install a Puppeteer-compatible browser during the image build, make its cache and profile writable, check shared libraries, and configure Browsershot with explicit paths only when discovery is unreliable.
Understand the Sail runtime boundary first
Laravel Sail is a command-line interface for the Docker services defined by your Laravel project. Commands run in containers, not in the host operating system. Browsershot is invoked by PHP, so the Node binary, npm, Puppeteer package, browser executable, shared libraries, and writable runtime directories must be present in the application container that handles the request, queue job, or command.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Start every diagnosis from that container. Use your project’s Sail wrapper rather than testing only on the host:
#1 Best Overall
./vendor/bin/sail shell
whoami
pwd
printenv HOME
which node
node --version
which npm
npm --version
php -v
If your project uses a different Sail command wrapper, use that equivalent command. Compare the user shown in an interactive shell with the user used by PHP-FPM, a queue worker, or a scheduler. A browser downloaded under one home directory may be invisible to a process running with another HOME.
Check the versions before changing configuration
Browsershot v4 has its own requirements, while Puppeteer’s browser-management behavior varies by package version. Record the installed versions before applying a recipe copied from another project:
./vendor/bin/sail composer show spatie/browsershot
./vendor/bin/sail npm list puppeteer --depth=0
./vendor/bin/sail npm list @puppeteer/browsers --depth=0
If a package is installed in a subdirectory, run the npm command from that directory. Keep the Puppeteer package, downloaded browser, cache location, and image build aligned. A community Sail recipe that worked with one package or image is not a universal configuration.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsVerify Node and npm as Browsershot sees them
Browsershot normally calls node and npm by command name. If those commands work in your shell but fail from a web request, the process PATH is different. Locate the binaries in Sail:
./vendor/bin/sail shell
command -v node
readlink -f "$(command -v node)"
command -v npm
readlink -f "$(command -v npm)"
Configure the absolute paths in your Browsershot code when PATH differences are intentional:
use SpatieBrowsershotBrowsershot;
Browsershot::url('https://example.com')
->setNodeBinary('/absolute/path/to/node')
->setNpmBinary('/absolute/path/to/npm')
->save('/tmp/example.png');
Replace the example paths with values confirmed inside the container. Do not assume a host path or a path from a different image.
Install Puppeteer’s browser in a repeatable way
Puppeteer usually downloads a browser compatible with its package. That download is not guaranteed if package-manager lifecycle scripts were disabled, dependencies were installed in a different image layer, or the build user and runtime user have different home directories.
Recommended Free Tools
Check the configured cache
Puppeteer uses a cache under the home directory by default. Configuration or PUPPETEER_CACHE_DIR can move it. Inspect the value used by the image and runtime:
./vendor/bin/sail shell
printf 'HOME=%sn' "$HOME"
printf 'PUPPETEER_CACHE_DIR=%sn' "$PUPPETEER_CACHE_DIR"
find "${PUPPETEER_CACHE_DIR:-$HOME/.cache/puppeteer}" -maxdepth 3 -type f 2>/dev/null | head
Choose one cache directory, set it during the image build and at runtime, and ensure the executing user can read and execute its contents. Avoid downloading Chrome every time a container starts; install it while building the image so deployments are deterministic and startup is faster.
Run the supported installer when scripts did not run
If the browser is absent, run Puppeteer’s browser installer from the application container:
./vendor/bin/sail npx puppeteer browsers install
Use the package documentation that matches your installed version if the command’s options differ. Then verify that the downloaded executable exists in the configured cache and is accessible to the runtime user. In production, put the equivalent install step in the Dockerfile or image build rather than relying on an interactive repair.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Keep build and runtime users consistent
A frequent “Could not find Chrome” pattern is a browser downloaded as root during the build and a web worker running as an unprivileged user with another home directory. Either install and run under the same user or place the cache in a shared, readable location and set the cache variable for both stages. Check ownership and mode bits:
Rank #3
./vendor/bin/sail shell
ls -ld "${PUPPETEER_CACHE_DIR:-$HOME/.cache/puppeteer}"
find "${PUPPETEER_CACHE_DIR:-$HOME/.cache/puppeteer}" -type f -name 'chrome*' -exec ls -l {} ; | head
Use an explicit Chrome path when discovery is the problem
You can use Puppeteer’s downloaded browser or a system-installed Chrome/Chromium. If you use a system browser, verify it inside Sail:
./vendor/bin/sail shell
command -v google-chrome || true
command -v chromium || true
command -v chromium-browser || true
ls -l /path/to/browser
/path/to/browser --version
Then configure Browsershot:
use SpatieBrowsershotBrowsershot;
Browsershot::url('https://example.com')
->setChromePath('/path/to/browser')
->save('/tmp/example.png');
The executable must exist and be executable in the container, and its version must be compatible with the Puppeteer package. Avoid hard-coding a versioned Puppeteer cache path: a browser update can change that path. Prefer a stable configured cache or a stable system executable.
Read the launch error literally
“Could not find Chrome”
- Confirm the browser-install lifecycle script was allowed to run.
- Run
npx puppeteer browsers installin Sail if the cache is empty. - Compare
HOME,PUPPETEER_CACHE_DIR, and the runtime user between image build and execution. - Use
setChromePath()only after confirming a valid executable inside the container.
“error while loading shared libraries”
The browser file can exist and still fail before startup. Puppeteer recommends checking unresolved dynamic libraries:
./vendor/bin/sail shell
ldd /path/to/chrome | grep not
Install the missing dependencies for the Linux distribution in your Sail image. Library names differ between distributions; do not blindly paste an Ubuntu package list into an Alpine-based image. A missing libnss3 has been reported in Sail environments, but it is one example rather than a universal diagnosis.
Permission, profile, or crashpad errors
Chrome needs writable cache, configuration, profile, and crash-report locations. Check the effective user, home directory, mounted volumes, and temporary directory. Give the runtime user ownership of the selected writable directories or configure explicit writable paths. A read-only mount can produce a crash that looks like a browser incompatibility.
Sandbox errors
Sandboxing is a container security decision, not a general repair. If your container security model supports Chrome’s sandbox, configure the required permissions and keep sandboxing enabled. Browsershot provides a no-sandbox option for restricted environments:
Rank #4
Browsershot::url('https://example.com')
->noSandbox()
->save('/tmp/example.png');
Use this only when the environment requires it and you understand the isolation trade-off. Puppeteer’s Docker guidance is designed for sandboxed operation and expects the container to have the necessary SYS_ADMIN capability. Adding --no-sandbox will not install a missing browser or repair missing libraries.
No usable browser process or wrong executable
- Print the Node, npm, and Chrome paths from inside Sail.
- Check executable permissions and unresolved libraries.
- Compare the browser version with the installed Puppeteer version.
- Verify that the same user and environment are used by HTTP requests and queue workers.
Build a repeatable Sail image
A durable setup has one documented build path: install Node dependencies, install the Puppeteer browser during the image build, set a known cache directory, install distribution-appropriate libraries, and run the application under a user that can read the browser and write its profile and temporary files. Pin dependency versions according to your project’s normal lockfile policy. At deploy time, verify the image rather than downloading a new browser on every request.
Include a smoke test in deployment or health checks that runs from Sail and captures a small page. The test should exercise the same user and environment as the real web or queue process. If the smoke test passes but production requests fail, compare environment variables, PATH, mounted directories, and process users before changing browser flags.
Choose local Chrome or a separate browser service
| Approach | What you operate | When it fits | Trade-offs |
|---|---|---|---|
| Browser in the Sail application container | Node, Puppeteer, Chrome, Linux libraries, cache, writable storage, and container security | Development parity and rendering inside the application environment | More image maintenance and resource contention |
| Separate or hosted browser service | Network access, credentials, and the service client or driver | Isolation from application-image browser dependencies | Additional service configuration, network failure modes, and hosting constraints |
Laravel Sail documents Selenium as a browser-testing service for Dusk; that is a distinct integration pattern from Browsershot rendering. Spatie’s Laravel Screenshot documentation also describes Cloudflare Browser Rendering as a driver that avoids Node.js and Chrome in the application environment. Evaluate browser compatibility, operational ownership, latency, network access, and where credentials and data are allowed to go.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server when you do not want Chrome dependencies in Sail. It accepts a URL and returns PNG, JPEG, WebP, or PDF. Before capture it accepts cookie or consent banners 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 response headers identify the page verdict and billing status.
One request is enough. See the complete option list and authentication details in the ScreenshotNeo 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}`);
ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. It supports full-page and element captures, device presets, custom viewports, dark mode, retina scale, PDFs, HTML/CSS rendering, JavaScript and CSS injection, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameters used by other screenshot APIs are accepted to ease migration.
Best Value
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 without adding a card.
FAQ
Should I install Chrome on my host machine?
No. Host Chrome is outside the Sail container and is not automatically available to PHP running there.
Why does an interactive Sail shell work while a queue job fails?
The job may use a different user, HOME, PATH, cache, mounted filesystem, or environment variables. Compare those values inside the worker’s actual runtime.
Is --no-sandbox the standard fix?
No. It addresses a sandbox-permission decision only. Missing browsers, libraries, paths, and writable directories require separate fixes.
Frequently Asked Questions
Can I share Puppeteer’s browser cache between Sail containers?
Yes, if the cache path is configured consistently and the runtime user can read and execute it. Treat the cache as part of the image or a deliberately managed shared volume.
What should I do when Chrome updates and the configured path changes?
Use a stable system executable or a consistently configured Puppeteer cache instead of a hard-coded, versioned cache directory; verify the path during image validation.
The Bottom Line
Diagnose Browsershot from inside the Sail container, align Puppeteer, its browser cache, Linux libraries, users, and writable directories, and treat sandbox flags as security configuration rather than a shortcut. If maintaining that browser stack is not worthwhile, ScreenshotNeo removes the container browser dependency while reporting whether a capture was clean and billable.
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.

