October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
Chrome

How to Fix Puppeteer Screenshot Failures on Heroku

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

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.

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

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.

  1. Open your app’s Settings in the Heroku Dashboard and inspect Buildpacks, or run heroku buildpacks --app YOUR_APP.
  2. Ensure the Puppeteer dependency buildpack is present. Add it with heroku buildpacks:add --index 1 https://github.com/jontewks/puppeteer-heroku-buildpack --app YOUR_APP if it is absent.
  3. 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.
  4. 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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
heroku 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
Sale
HTML and CSS: Design and Build Websites
  • 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:

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

{
  "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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
heroku 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 chrome fails, 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
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • 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
  1. Log Node memory around each job with process.memoryUsage().
  2. Close every page in a finally block and close the browser when the worker is finished.
  3. Prefer a bounded queue over launching one browser per request.
  4. Measure the safe number of concurrent pages on the selected dyno instead of deriving worker count from CPU count alone.
  5. 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.

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

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

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.

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

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.

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.

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

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.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.