ScreenshotNeo

BlogHow-to

How to fix Playwright screenshot tests failing in Docker on Ubuntu

Diagnose Playwright screenshot failures in Docker on Ubuntu, from missing browser dependencies and Chromium crashes to visual snapshot mismatches.

By the ScreenshotNeo team4 October 20269 min read

Start by identifying which kind of failure you have: a browser launch or executable error, a Chromium crash, a headed-mode display error, or a visual snapshot mismatch. For launch errors, align the Playwright package, browser binaries, and Docker image, then install the Linux dependencies. For visual diffs, run the test in the same environment used to create its baseline and inspect the images before updating snapshots.

The title does not include an error message, Dockerfile, Playwright version, or failing assertion, so there is no single root cause to assume. Follow the matching path below and use the logs and actual/expected images as evidence.

1. Classify the failure before changing configuration

What you see Likely category First check
Executable missing, browser launch error, or missing shared library Browser setup Installed Playwright version, matching browser installation, and Linux system dependencies.
Chromium starts and then exits or crashes in the container Container runtime or resources Browser logs and whether Docker is using --init and --ipc=host.
Test reports that the screenshot does not match its snapshot Visual comparison Baseline environment, browser version, fonts, headless mode, and actual/expected images.
Headed browser cannot connect to a display Display server Whether Xvfb is installed and the test is run under it.

A failed visual assertion is not evidence that Chromium failed to launch. Conversely, installing fonts or updating snapshots will not fix a missing browser executable.

2. Align Playwright, browser binaries, and the Docker image

Playwright browser binaries are tied to Playwright releases. Pin the Docker image to a version that matches the Playwright package used by the project, and install the browser for that package version. Playwright’s Docker image includes browser binaries and their system dependencies, but the Playwright package still needs to be installed in the project. A mismatch between the image and package can leave Playwright unable to find its expected executable. See the official Docker guidance and browser installation documentation.

Check the project version

npx playwright --version

Use the reported version to select the matching Playwright Docker image tag or to install its browser binaries in your custom image. Do not upgrade to a version from an example without checking the version your project requires.

Install Chromium and its Linux dependencies in a custom image

For an existing Node project, a minimal pattern is to install the locked project dependencies first and then install Chromium with the matching Playwright CLI:

FROM node:20-bookworm
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npx playwright install --with-deps chromium
CMD ["npx", "playwright", "test"]

This example uses a Debian-based Node image. It demonstrates the install sequence; choose a base image and Playwright version compatible with your project. For a custom image, ensure Node.js, the Playwright package, browser binaries, and browser system dependencies are all present. The documented Linux install form is npx playwright install --with-deps chromium.

Install the browser in an existing container

npx playwright install --with-deps chromium

Replace chromium with the browser your tests use, or install all supported browsers with npx playwright install --with-deps. In CI, prefer performing installation in the image build so test runs are repeatable and do not depend on a mutable container state.

3. Check Docker runtime settings for Chromium crashes

When Chromium launches but crashes or behaves unreliably inside Docker, try the runtime options Playwright documents for container execution:

docker run --init --ipc=host your-playwright-image
  • --init adds an init process to handle process lifecycle and help avoid zombie processes.
  • --ipc=host gives Chromium more shared memory and can reduce memory-related crashes.

These settings target applicable container process and memory symptoms. They are not fixes for screenshot pixel differences. For weird Chromium launch errors during local development, Playwright also suggests trying --cap-add=SYS_ADMIN as a diagnostic. Treat it as a symptom-specific troubleshooting step, not a default for every test container. See Playwright’s Docker recommendations.

4. Make visual snapshot tests repeatable

If the browser runs and toHaveScreenshot() fails, compare the baseline and current run in the same environment. Playwright documents that rendering may vary with host operating system, browser version, settings, hardware, power source, and headless mode. Different browsers and platforms can also render fonts and page content differently. These are reasons a diff can appear without an application regression. Read Playwright’s visual comparison guidance.

Keep baseline generation and CI aligned

  1. Use the same OS/container image, Playwright package, browser, and headless mode to create and verify snapshots.
  2. Keep the relevant fonts and browser settings consistent between baseline generation and CI.
  3. When you intentionally support multiple browser or platform combinations, maintain separate projects or baselines as needed rather than comparing unlike environments.
  4. Inspect the expected and actual screenshots for the failing assertion before deciding whether the UI changed unexpectedly.

Playwright’s toHaveScreenshot() assertion captures until two consecutive screenshots match before saving a new reference. This helps with capture stability, but it cannot make differences between environments disappear.

Handle intentionally volatile content carefully

For page regions that are deliberately outside the visual contract, Playwright supports a stylePath stylesheet option for screenshot assertions. For example, a project can hide a timestamp that changes on every run:

import { test, expect } from '@playwright/test';

test('account page is visually stable', async ({ page }) => {
  await page.goto('http://127.0.0.1:3000/account');
  await expect(page).toHaveScreenshot('account.png', {
    stylePath: './tests/screenshot-stability.css',
  });
});
/* tests/screenshot-stability.css */
.last-updated-timestamp {
  visibility: hidden !important;
}

Use this only for content intentionally excluded from the visual contract. Hiding a meaningful region can conceal a real regression. Consult the snapshot assertion options for the options supported by your installed Playwright version.

Update snapshots only after reviewing the change

If the visual change is intentional, regenerate baselines with:

npx playwright test --update-snapshots

Review the resulting image changes and commit the reviewed snapshots. Blindly accepting generated diffs can turn a real UI regression into the new baseline.

5. Collect browser launch diagnostics

For launch failures, enable Playwright browser logging and rerun the failing test:

DEBUG=pw:browser npx playwright test

Use the first relevant launch or missing-library message to distinguish an executable/version problem from a container runtime issue. The CI documentation describes browser debugging and Linux display setup.

Run headed tests on Linux with Xvfb

A headed browser needs a display server. On Linux, run the test under Xvfb:

xvfb-run npx playwright test

Playwright’s Docker image and GitHub Action include Xvfb. If you use a custom image, make sure Xvfb is installed before using this command. If the test is intended to run headless, check that the test configuration is not unexpectedly launching a headed browser.

Inspect what the installer will do

When dependency installation is unclear, the CLI can show the planned actions without applying them:

npx playwright install --dry-run
npx playwright install-deps --dry-run

On Linux, the dependency dry run simulates apt-get actions and can exit nonzero when packages are missing. See the Playwright CLI reference.

6. A practical Dockerfile and run checklist

Use this checklist to isolate common setup errors:

  1. Confirm npx playwright --version inside the same image that runs the tests.
  2. Confirm the image tag and installed browser binaries match that Playwright release.
  3. Install the browser and Linux dependencies with npx playwright install --with-deps chromium, or use the matching official Playwright image.
  4. For Chromium crashes, run with --init --ipc=host and inspect browser logs.
  5. For headed Linux runs, provide Xvfb and invoke xvfb-run.
  6. For visual diffs, compare baseline and test environment, then inspect actual and expected images.
  7. Regenerate snapshots only after confirming the rendered change is intentional.

For a custom image, keep dependency installation in the Docker build and pin image tags. This makes the browser setup easier to reproduce across local and CI runs.

7. Troubleshooting common errors

Symptom Cause to investigate Fix
Playwright cannot find a browser executable The image and project use different Playwright versions, or the browser was not installed. Align the package and image versions, then install the browser for the project’s Playwright version.
Browser exits with a missing shared library or dependency message Linux browser system dependencies are absent from the custom image. Run npx playwright install --with-deps chromium in the build, or use an image with the matching dependencies.
Chromium crashes in Docker Container process handling or shared-memory limits may be contributing. Try --init --ipc=host, collect DEBUG=pw:browser logs, and verify available container resources.
Odd Chromium launch error during local development A container runtime restriction may be involved. As a diagnostic, try the Docker capability option documented by Playwright: --cap-add=SYS_ADMIN.
Headed browser reports no display No X server is available in the Linux container. Install/use Xvfb and run xvfb-run npx playwright test.
Screenshot assertion fails only in Docker or CI Baseline and test environment may differ in OS, browser, fonts, settings, or headless mode. Run both in the same environment or maintain environment-specific baselines; inspect the image diff.
Screenshot differs on every run The page may contain volatile content, or consecutive captures may not have stabilized. Wait for the page’s relevant state and use a narrowly scoped stylePath only for intentionally volatile regions.
Snapshot update makes failures disappear, but the change is unclear The new baseline may have accepted an unintended regression. Review the generated screenshot diffs and restore any unexplained changes before committing.

8. Performance, reliability, and cost notes

  • Build time: Installing browsers and OS packages adds image build work. Doing it once in a pinned image avoids repeating setup during every test invocation.
  • Runtime reliability: Matching browser and package versions makes executable discovery predictable. A consistent baseline environment reduces visual noise.
  • Container memory: Chromium can be sensitive to shared-memory constraints; --ipc=host is a documented option to reduce memory-related crashes. Check the resource behavior when diagnosing a crash.
  • Visual stability: Suppressing volatile content may reduce noise, but broad hiding rules weaken what the test verifies. Keep exclusions specific and reviewable.
  • Cost: The dossier provides no benchmark or cost figures for Docker execution. In practice, CI cost depends on the runner, image build, test duration, and resource allocation; measure those in your own pipeline rather than assuming a fixed saving.

9. Or skip the browser setup

If your goal is to capture a website screenshot rather than run a browser-based visual regression test, ScreenshotNeo provides a website screenshot API and MCP server. A single request returns an image or PDF. See the ScreenshotNeo API 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}`);
  • Cookie banners are accepted and removed before capture, along with known newsletter popups and chat widgets; each step can be turned off.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents.
  • The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month with no card.

10. FAQ

Should I use the official Playwright image?

It is a straightforward option when its tag matches the Playwright package in your project. A custom image also works when it includes Node.js, the package, matching browser binaries, and Linux dependencies.

Should I update snapshots after every CI failure?

No. First establish that the baseline and test run use the intended environment, then inspect the visual difference. Update only for an intentional change.

Does ScreenshotNeo replace Playwright visual regression tests?

No. ScreenshotNeo captures pages through an API; Playwright’s test assertion compares rendered output against committed baselines. Use the approach that matches whether you need a capture service or a browser-driven regression test.

Sources