Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Start every diagnosis from that container. Use your project’s Sail wrapper rather than testing only on the host:

./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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Verify 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

./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 install in 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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
./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:

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.