ScreenshotNeo

BlogHow-to

How to Fix Puppeteer Screenshot Tests Failing on an Ubuntu Server in India

Separate Chrome launch failures from visual mismatches, then fix the Ubuntu, browser, or screenshot condition causing Puppeteer tests to fail.

By the ScreenshotNeo team4 October 20268 min read

First identify where the test fails: Chrome launch, page navigation, screenshot capture, or image comparison. A launch failure needs a browser, library, sandbox, or permission fix; a pixel mismatch needs the baseline and capture conditions made consistent. The available Puppeteer documentation does not identify India itself as a cause. Treat location as relevant only if you find a specific difference in locale, timezone, fonts, or network access.

Before changing flags or updating baselines, keep the complete error and record your Puppeteer version, its browser version, Node version, Ubuntu release and CPU architecture, install command, launch flags, runtime user, writable paths, and CI or container setup. Puppeteer’s current requirements list Node 22.12+ and Debian/Ubuntu x64 and arm64 support; check the requirements for your installed Puppeteer release before upgrading an established project. Puppeteer system requirements.

1. Classify the failure

Locate the failing step in the test and preserve its error output. These categories point to different fixes:

Failure point Typical clues Start by checking
puppeteer.launch() Missing executable, shared library error, sandbox error, browser process exits Browser install/cache, Linux libraries, host sandbox policy, writable profile paths
Navigation or page readiness Navigation timeout, unexpected response, app content missing URL access from the server, app readiness condition, network and request blocking
page.screenshot() Capture throws or file is absent Page state, screenshot options, filesystem permissions and destination path
Image comparison Both images exist, but the comparison reports changed pixels Browser build, viewport, fonts, loaded assets, page state, and capture options

Record the exact test command and whether it runs in Docker or CI. Keep a copy of the generated image and browser logs so you can tell whether the fix addressed the cause.

2. Check the browser install and Linux libraries

Puppeteer normally downloads a compatible Chrome for Testing browser. Since Puppeteer v21.6.0, its browser downloads also include chrome-headless-shell. The default browser cache has been $HOME/.cache/puppeteer since v19.0.0. If package-manager policy blocked install scripts, the browser may be missing even though the Node package is present. See the Puppeteer installation guide.

Install the browser explicitly using Puppeteer’s browser installer when appropriate:

npx puppeteer browsers install

Make sure the build or deploy user and the test runtime user can access the same browser installation and cache. If they differ, configure a browser location or cache directory that is available to the runtime user instead of assuming its home directory is shared.

If Chrome exits because a shared library is missing, inspect the actual binary. Replace /path/to/chrome with the path to the executable used by your test:

ldd /path/to/chrome | grep not

Puppeteer’s troubleshooting guide lists common Debian and Ubuntu dependencies involving certificates, fontconfig, GBM, GTK, NSS, Pango, and X11 libraries. The exact package names can vary with Ubuntu and browser revisions, so use the current troubleshooting guidance and its linked Chromium dependency list to resolve the libraries reported missing by your binary.

The browser installer’s installDeps option supports installing Chrome dependencies on Debian or Ubuntu, but it invokes apt-get and requires system privileges. Use it during provisioning only if that is appropriate for your environment; do not assume a restricted CI job can run it.

3. Diagnose sandbox errors on the host

If the error says No usable sandbox!, investigate the host policy and browser location before changing launch flags. Puppeteer documents a specific issue on Ubuntu 23.10 and later: AppArmor policy can prevent Puppeteer-downloaded Chrome for Testing from using user namespaces. Follow the host-specific guidance linked from the Puppeteer Linux sandbox troubleshooting section.

Puppeteer warns: “Running without a sandbox is strongly discouraged.” Avoid making --no-sandbox the default workaround, especially for a runner that visits untrusted pages. Prefer a configuration that lets Chrome use its sandbox. If you are evaluating a temporary diagnostic change, understand that it changes the browser’s security posture and is not a general repair for missing libraries, permissions, or AppArmor policy.

4. Fix profile and filesystem permissions

Chrome writes profile, configuration, and cache files during startup. In a read-only container or on a host with restricted mounts, the browser may fail before Puppeteer connects. Check which user runs the test and whether that user can write to the browser’s profile, cache, temporary directory, and screenshot output directory.

If the default profile location is not writable, set userDataDir to an explicit writable directory in your launch configuration, or mount writable directories owned by the Chrome user. Ensure the directory exists and has the right ownership in the actual runtime environment, not just in the build image. Puppeteer’s troubleshooting guide covers read-only containers and writable profile paths.

5. Make visual comparisons repeatable

If Chrome launches and produces an image, treat a comparison failure as a consistency problem until you establish otherwise. Keep the Puppeteer and browser versions paired: each Puppeteer release is bundled with a specific browser release for protocol compatibility. Use the same browser build for baseline generation and CI. The Puppeteer FAQ explains the version pairing.

Normalize the conditions that can change pixels:

  • Viewport and screen: Headless screen defaults to 800 × 600 unless configured. Set the same viewport and window dimensions in both runs; also keep device emulation and device scale factor consistent.
  • Capture area: Use the same clipped or full-page behavior and the same clip rectangle. Full-page capture can expose content that is outside the viewport.
  • Image options: Keep image type and quality consistent. Options include fullPage, clip, captureBeyondViewport, omitBackground, type, and quality. See ScreenshotOptions and Page.screenshot().
  • Fonts: Install the same fonts on baseline and CI hosts. Missing fonts or different font versions can change glyphs, line breaks, and element dimensions. Choose fonts based on the page’s CSS font stack and the scripts it actually displays; do not infer required fonts from the server’s country.
  • Page state and assets: Wait for application-specific content and relevant images, fonts, and stylesheets. The same URL can render differently if data, animation, time, or network-loaded assets differ.
  • Background: Keep transparent-background behavior consistent. omitBackground changes what is captured behind transparent page content.

Do not update the expected screenshot just to silence a diff until you have checked for a real application change and environment drift. Change one condition at a time, run the one failing case, save its output, then run the relevant suite.

6. Use a minimal Puppeteer capture to isolate the problem

This runnable Node.js example separates launch, navigation, and capture errors. It uses the Puppeteer package’s compatible browser by default. Set the viewport explicitly, wait for the page to load, and write the image to a known path:

const puppeteer = require('puppeteer');

(async () => {
  let browser;
  try {
    browser = await puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    await page.setViewport({ width: 1280, height: 800, deviceScaleFactor: 1 });
    await page.goto('https://example.com', { waitUntil: 'networkidle0', timeout: 60000 });
    await page.screenshot({ path: 'shot.png', type: 'png', fullPage: true });
    console.log('Saved shot.png');
  } catch (error) {
    console.error('Puppeteer capture failed:', error);
    process.exitCode = 1;
  } finally {
    if (browser) await browser.close();
  }
})();

Run it as the same operating-system user and inside the same container or CI image as the failing test. If it fails before printing the output path, focus on launch, navigation, or capture. If it saves an image, compare that image with the baseline and investigate rendering conditions.

7. Troubleshooting common errors

Symptom Likely cause Fix
Could not find Chrome or executable missing Browser install script was skipped, or runtime user cannot see the install/cache Run npx puppeteer browsers install during provisioning; verify the browser path and cache as the test user.
error while loading shared libraries Ubuntu image lacks a library required by the Chrome binary Run ldd /path/to/chrome | grep not; install the matching dependencies listed in current Puppeteer/Chromium guidance.
No usable sandbox! Sandbox support or host policy, including possible Ubuntu AppArmor restrictions Check Ubuntu version, browser source/location, and AppArmor guidance; retain Chrome sandboxing where possible.
Browser starts locally but not in container Different runtime user, read-only filesystem, missing writable profile/cache/temp path Use a writable userDataDir and mount paths with ownership matching the Chrome process.
Navigation timeout Server cannot reach the URL, page never reaches the selected load condition, or the application is slow Check the URL from the same host/container and wait for the application’s actual readiness condition; avoid assuming network idle is appropriate for pages with continuous requests.
Text wraps differently or glyphs are missing Different or absent Linux fonts Install the site’s required font files consistently in baseline and CI, then regenerate a baseline only if the intended rendering changed.
Small widespread pixel differences Different browser build, viewport, device scale, font rendering, or image options Pin compatible versions and align viewport, device settings, capture options, and host fonts.
Image is blank or page content is incomplete Capture occurred before app content or assets were ready, or requests failed Wait for a meaningful selector or application readiness signal; inspect failed requests and the saved screenshot.

8. Notes on reliability, performance, and cost

For repeatability, provision the browser and OS dependencies in the same image used by CI, keep the Puppeteer/browser pair stable, and run tests under the same user and filesystem permissions. Preserve browser logs and failed screenshots as CI artifacts. When diagnosing a failure, change one environmental variable at a time so the result points to a cause.

Browser downloads and system dependencies add setup time and image size; caching the compatible browser can avoid repeated downloads, provided the cache is available to the runtime user. Waiting for a broad network-idle condition can make tests slower or hang on pages with persistent connections, so wait for the narrowest reliable application readiness condition. No particular India-region performance penalty is established by the cited Puppeteer documentation; measure the actual server-to-target path if navigation speed is the symptom.

For cost, account for CI minutes, browser provisioning, and maintenance of a stable test image. The diagnostic remedies here do not require buying server hardware or a browser accessory. A screenshot API can be an alternative when the task is capturing a URL rather than exercising your own Puppeteer test logic.

Or skip the browser setup

If your goal is to capture a URL rather than debug a browser test, ScreenshotNeo returns a screenshot or PDF from one API request. Its API accepts screenshots in PNG, JPEG, or WebP. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo removes cookie banners, popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for free screenshots.

FAQ

Does running the test on an Ubuntu server in India require a special Puppeteer flag?

There is no India-specific flag in the reviewed Puppeteer guidance. Look for evidence in your server’s locale, timezone, fonts, or network path before treating region as the cause.

Should I update the browser every time Puppeteer updates?

Use the browser version paired with your Puppeteer release and keep baseline generation aligned with CI. When changing versions, review whether the resulting visual changes are expected before updating baselines.

When should I update a screenshot baseline?

After confirming the application change is intentional and the baseline and CI environments use the same browser, fonts, viewport, page state, and screenshot options.