The reliable fix is diagnostic, not a single launch flag: install the browser’s Linux dependencies, verify that Chrome exists in the slug and on PATH, run headless Chrome with --no-sandbox, preserve Puppeteer’s browser cache when your buildpack requires it, and investigate memory when logs show R14. A screenshot that works on a laptop can still fail on a Heroku dyno because the dyno is a Linux server without a GUI or desktop dependencies.
Start with the exact failure
Do not change five settings at once. Save the deployment log, the runtime exception, and the complete stack trace first. The message usually places the failure in one of four branches.
“Could not find Chrome” or “Could not find Chromium”
The browser was not installed during the build, its cache was omitted from the slug, or Puppeteer is looking at a different executable path. Check the buildpack output, the deployed cache directory, the package version, and the executable path before changing launch flags.
Chrome exits immediately
Missing shared libraries are common on Heroku. Confirm that a Puppeteer-compatible dependency buildpack or Chrome installation ran, then use headless mode and --no-sandbox. Optional flags such as --disable-gpu or --remote-debugging-port=9222 should be added only when the observed error or selected integration calls for them.
#1 Best Overall
It works locally but not after deployment
Compare the deployed Puppeteer version, browser version, cache location, buildpack list and order, Node runtime, and environment variables with your local machine. A locally installed desktop Chrome does not prove that the Heroku slug contains a compatible browser or its Linux libraries.
It fails under load or Heroku reports R14
R14 means the Node process exceeded the dyno’s memory quota. Heroku reports that excess memory is paged to slower disk. Treat this as evidence of memory pressure, not proof of a Puppeteer bug; measure process memory, concurrency, browser count, and whether pages and browsers are closed.
Install the browser and Linux dependencies
Puppeteer’s troubleshooting documentation says Heroku’s Linux environment does not include the additional dependencies required by Puppeteer by default. Its documented Heroku route points to the community buildpack puppeteer-heroku-buildpack.
- Open your app’s Settings in the Heroku Dashboard and inspect Buildpacks, or run
heroku buildpacks --app YOUR_APP. - Ensure the Puppeteer dependency buildpack is present. Add it with
heroku buildpacks:add --index 1 https://github.com/jontewks/puppeteer-heroku-buildpack --app YOUR_APPif it is absent. - Trigger a fresh deploy and read the build output. Look for the dependency installation step; a buildpack listed in configuration but absent from the build log is not a successful installation.
- Keep your primary language buildpack last in a classic multiple-buildpack setup. Heroku’s buildpack ordering rules use the last buildpack for the main language compilation, so inspect the actual order rather than adding a second Chrome buildpack blindly.
An alternative is Heroku’s Chrome for Testing buildpack. It installs Chrome and ChromeDriver, uses Google’s Stable channel by default, and places chrome and chromedriver on the dyno’s PATH. Its documented absolute paths can change, so prefer runtime discovery:
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 problemsheroku run bash --app YOUR_APP
which chrome
chrome --version
which chromedriver
chromedriver --version
Use one installation route that matches your app and verify compatibility with the Puppeteer package. The Puppeteer-specific buildpack focuses on dependencies needed to run Puppeteer; the Chrome for Testing route focuses on Chrome and ChromeDriver. Neither route is universally correct without knowing your existing buildpacks and versions.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Launch Chrome correctly on a dyno
Heroku dynos have no graphical desktop. Keep Puppeteer headless and include the documented sandbox argument:
const puppeteer = require('puppeteer');
async function capture(url) {
const browser = await puppeteer.launch({
args: ['--no-sandbox']
});
try {
const page = await browser.newPage();
await page.goto(url, {waitUntil: 'networkidle2', timeout: 60000});
await page.screenshot({path: 'shot.png', fullPage: true});
} finally {
await browser.close();
}
}
capture('https://example.com').catch(error => {
console.error(error);
process.exitCode = 1;
});
The community buildpack also advises retaining Puppeteer’s default headless behavior. Chrome for Testing documentation lists --headless and --no-sandbox as typical flags and notes that some cases may need --disable-gpu or --remote-debugging-port=9222. Do not treat those optional flags as guaranteed fixes: first establish whether the failure is dependency, executable, navigation, or memory related.
Use an explicit executable path only when you have verified it
If which chrome returns a path, pass that path deliberately:
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →const browser = await puppeteer.launch({
executablePath: process.env.CHROME_BIN || 'chrome',
args: ['--no-sandbox'],
headless: true
});
Do not copy an absolute path from an old build log. The Chrome for Testing buildpack warns that its absolute binary location may change; a PATH-based command or a runtime-discovered path is safer.
Preserve Puppeteer’s browser cache
Puppeteer 19 and later changed browser caching. The community buildpack README documents moving /app/.cache/puppeteer into the application cache during heroku-postbuild; otherwise the slug can deploy without Chromium.
Rank #3
{
"scripts": {
"heroku-postbuild": "mkdir ./.cache && mv /app/.cache/puppeteer ./.cache"
}
}
Adapt that example to your existing build script and verify both paths in the build log. If /app/.cache/puppeteer does not exist, do not hide the error with a blind move: check the installed Puppeteer version, the buildpack’s cache behavior, and the value of PUPPETEER_CACHE_DIR. Puppeteer documents PUPPETEER_CACHE_DIR for selecting a cache directory when the default is unsuitable.
When browser startup or missing-library symptoms appear after repeated deploys, the community buildpack README suggests clearing Heroku’s build cache and rebuilding. That is a troubleshooting step, not evidence that cache corruption is the root cause:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchheroku plugins:install @heroku-cli/plugin-builds
heroku builds:cache:purge --app YOUR_APP
git push heroku main
Use the cache command supported by your Heroku CLI; if the plugin is unavailable, purge the cache through the Dashboard or current Heroku tooling.
Verify the deployed runtime
Run checks in a one-off dyno so you inspect the same filesystem and environment used by the web process:
heroku run bash --app YOUR_APP
node --version
npm list puppeteer --depth=0
which chrome
printenv PUPPETEER_CACHE_DIR
ls -la /app/.cache/puppeteer
- If
which chromefails, fix installation or buildpack order before changing JavaScript. - If the cache directory is empty, correct the build script or cache variable.
- If Puppeteer and Chrome versions differ from your lockfile or local test, pin and deploy the intended package versions together.
- If the command works in a one-off dyno but the web process fails, compare config vars, release phase behavior, process type, and the command in your
Procfile.
Control memory, pages and concurrency
A browser is a substantial process, and each page, loaded asset, and simultaneous job adds pressure. Heroku’s R14 message is emitted when the Node application uses more memory than the dyno provides; paging then slows the process and can turn navigation into a timeout.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
- Log Node memory around each job with
process.memoryUsage(). - Close every page in a
finallyblock and close the browser when the worker is finished. - Prefer a bounded queue over launching one browser per request.
- Measure the safe number of concurrent pages on the selected dyno instead of deriving worker count from CPU count alone.
- Reduce unnecessary full-page captures, large viewport sizes, and unneeded resource loads when the workload permits.
console.log({
rss: process.memoryUsage().rss,
heapUsed: process.memoryUsage().heapUsed,
external: process.memoryUsage().external
});
Heroku’s Node memory guidance describes WEB_MEMORY and derived WEB_CONCURRENCY settings for sizing processes. Apply those controls to the total memory budget of Node plus Chrome, not Node’s heap in isolation. A larger dyno can postpone R14, but it does not correct a leaked page or unbounded queue.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Common symptoms and targeted fixes
| Symptom | Likely check | Targeted action |
|---|---|---|
| Executable missing | Build log, cache directory, which chrome |
Install the appropriate buildpack and preserve the browser cache; correct the executable path. |
| Immediate Chrome exit | Shared-library errors and launch arguments | Install dependencies, keep headless mode, and add --no-sandbox. |
| Navigation timeout | Memory logs, URL behavior, network waits | Check R14, bound concurrency, and choose a wait condition appropriate to the page. |
| Blank screenshot | HTTP response, page console, bot check, failed resources | Capture console and page errors; confirm the target permits server-side browsing. |
| Only production fails | Buildpacks, versions, config vars and slug contents | Compare deployed runtime evidence with local versions; do not rely on desktop Chrome. |
Or skip the browser setup
For a service that returns screenshots without maintaining Chrome on your dyno, ScreenshotNeo provides a single GET request. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. It also offers an MCP server with take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
Use the API examples 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}`);
Every plan includes the features; the Free plan provides 1,000 shots a month with no card, Starter is $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing provides two months free. Create a free ScreenshotNeo account to start without a card.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.FAQ
Is --no-sandbox safe to omit if Chrome starts locally?
Local desktop behavior does not represent a Heroku dyno. Puppeteer’s Heroku guidance explicitly documents this argument; use it for the dyno integration you are deploying.
Should I install both Chrome buildpacks?
Not by default. Inspect the existing buildpack list and choose the route that matches your Puppeteer and browser setup; overlapping installations can make paths and caches harder to diagnose.
Best Value
Does R14 always mean a memory leak?
No. It means the dyno exceeded its memory quota. A leak, excessive concurrency, large pages, or an undersized dyno can all contribute.
Frequently Asked Questions
Is –no-sandbox safe to omit if Chrome starts locally?
Local desktop behavior does not represent a Heroku dyno. Puppeteer’s Heroku guidance explicitly documents this argument; use it for the dyno integration you are deploying.
Should I install both Chrome buildpacks?
Not by default. Inspect the existing buildpack list and choose the route that matches your Puppeteer and browser setup; overlapping installations can make paths and caches harder to diagnose.
Does R14 always mean a memory leak?
No. It means the dyno exceeded its memory quota. A leak, excessive concurrency, large pages, or an undersized dyno can all contribute.
The Bottom Line
Fix Heroku screenshot failures by proving where the failure occurs: install compatible Linux dependencies, verify Chrome and its cache in the slug, launch headless with --no-sandbox, and size concurrency for the dyno’s memory. The logs and one-off dyno checks should determine which branch applies.
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.




