---
title: "Running Playwright in Docker"
description: "Run Playwright in Docker: pick the right mcr.microsoft.com/playwright tag, use --ipc=host and --init, run as pwuser, and set up GitHub Actions."
canonical: "https://momentic.ai/blog/playwright-docker-guide"
last-updated: "2026-10-31T09:50:51Z"
---

# Running Playwright in Docker

URL: https://momentic.ai/blog/playwright-docker-guide

[blog](/blog) [/ resources](/blog/category/resources)  / playwright-docker-guide

Resources

How to run Playwright tests in Docker: image tags, the run flags Chromium needs, non-root users, a working Dockerfile and GitHub Actions.

Wei-Wei Wu

CEO, Momentic

## TL;DR

- Pin `mcr.microsoft.com/playwright:v<version>-noble` to the installed `@playwright/test` version. A mismatch can leave Playwright looking for browser binaries the image does not contain.
- Run the container with `--ipc=host` to give Chromium enough shared memory and `--init` to reap child processes. Without those flags, browser runs can crash or leave zombie processes.
- Run tests as the image’s non-root `pwuser`, and make sure the test directory and output paths are writable.
- Pin the same image tag in your GitHub Actions container job, and upload traces when tests fail.
- Generate screenshot baselines inside the CI container. Host fonts and rendering can produce different pixels even when the page behaves identically.

## Picking the right `mcr.microsoft.com/playwright` image tag

Playwright’s Docker image contains browser binaries and system dependencies, but your project still installs `@playwright/test` separately. Pin the image tag to the installed package version. Otherwise, Playwright may look for a browser revision that the image does not contain, and the test run can fail before a browser launches. [Playwright’s Docker documentation](playwright.dev/docs/docker) describes this version requirement.

Read the version installed in your project with the following command.

`node -p "require('@playwright/test/package.json').version"`

If it prints `1.52.0`, use `mcr.microsoft.com/playwright:v1.52.0-noble` and commit the lockfile that pins `@playwright/test`. The tag’s version identifies the Playwright release. Its suffix identifies the base Ubuntu release. `-noble` uses Ubuntu 24.04, while `-jammy` uses Ubuntu 22.04.

Keep the OS suffix fixed across local runs and CI. Changing the base image can change installed libraries and fonts even when the Playwright version stays the same, which matters when you compare screenshots.

## Docker run flags Playwright needs: `--ipc=host` and `--init`

Chromium can crash in a container when its processes run out of shared memory. Playwright [recommends `--ipc=host`](https://playwright.dev/docs/docker) because Chromium uses shared memory across processes, and Docker’s default IPC setup can leave too little available. A test may report `Target closed` after the browser exits. Host IPC does not fix a container-wide memory limit.

Docker also needs a process to reap child processes after they exit. The `--init` flag supplies one as PID 1, preventing zombie processes from accumulating during test runs. It addresses process cleanup, not the shared-memory crash.

Run both flags with an image tag that matches the `@playwright/test` version in your lockfile. For example, if your project pins `@playwright/test` to `1.56.1`, run the tests with the matching image.

`docker run --rm --init --ipc=host \  -v "$PWD:/work" -w /work \  mcr.microsoft.com/playwright:v1.56.1-noble \  sh -lc 'npm ci && npx playwright test'`

The bind mount makes your test project available at `/work`, and `npm ci` installs its locked dependencies inside the container. On a shared CI host, check whether your isolation policy permits `--ipc=host` before using it.

## Running the container as a non-root user

The official Playwright image runs as root by default. In CI, use its built-in `pwuser` so the test process does not run with root privileges. Match the image tag to the version of `@playwright/test` in your project.

`FROM mcr.microsoft.com/playwright:v1.56.0-noble WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN chown -R pwuser:pwuser /app USER pwuser CMD ["npx", "playwright", "test"]`

Add `node_modules` to `.dockerignore` so the copy step cannot replace the container’s installed dependencies. The `chown` lets `pwuser` write traces, screenshots, and test results under `/app`. If CI bind-mounts the project instead, the mounted directory’s host permissions take precedence. Grant the container user write access to the output directory rather than making the whole mount writable.

For an existing project directory, run the official image as `pwuser` directly.

`docker run --rm --init --ipc=host \  --user pwuser \  -v "$PWD:/app" -w /app \  mcr.microsoft.com/playwright:v1.56.0-noble \  npx playwright test`

Running as non-root does not automatically enable Chromium’s sandbox. If you enable that sandbox, supply a Docker seccomp profile that permits the required user-namespace operations. Keep the project and browser output directories writable by `pwuser` or use a custom UID that matches the mounted files.

## A working Dockerfile for a Playwright test project

A test project needs its Playwright package version to match the browser binaries in the image. The Dockerfile below uses `v1.55.0-noble` and stops the build if the committed lockfile installs a different `@playwright/test` version. It assumes your project has `package.json`, `package-lock.json`, and Playwright tests.

`FROM mcr.microsoft.com/playwright:v1.55.0-noble  WORKDIR /app  COPY package.json package-lock.json ./ RUN npm ci \  && node -e "if (require('@playwright/test/package.json').version !== '1.55.0') process.exit(1)"  COPY . . RUN chown -R pwuser:pwuser /app  USER pwuser CMD ["npx", "playwright", "test"]`

The base image supplies the browsers and their Linux dependencies, while `npm ci` installs the project dependencies from its lockfile. Copying dependency files before test code lets Docker reuse the install layer when only tests change. Add `node_modules` and `test-results` to `.dockerignore` so host files do not overwrite container files during `COPY . .`. The ownership change lets `pwuser` write test results inside `/app`. Running as `pwuser` does not, by itself, enable Chromium's sandbox.

Build and run the image from the project root.

`docker build -t playwright-tests . docker run --rm --init --ipc=host playwright-tests`

Docker starts a process reaper with `--init` and gives Chromium access to the host IPC namespace with `--ipc=host`. Those flags belong on `docker run`, not in the Dockerfile.

## Running Playwright containers in GitHub Actions

GitHub Actions can run the test job inside the same Playwright image you use locally. Pin the image to the installed `@playwright/test` version so the package and browser binaries match. The example below uses version `1.55.0` for both.

`# .github/workflows/playwright.yml name: Playwright  on:  push:  pull_request:  jobs:  e2e:  runs-on: ubuntu-latest  container:  image: mcr.microsoft.com/playwright:v1.55.0-noble  options: --ipc=host --init   steps:  - uses: actions/checkout@v4  - run: npm ci  - name: Check Playwright version  run: test "$(node -p "require('@playwright/test/package.json').version")" = "1.55.0"  - run: npx playwright test --trace=retain-on-failure  - uses: actions/upload-artifact@v4  if: always()  with:  name: playwright-test-results  path: test-results/  if-no-files-found: ignore`

The version check fails before the test suite runs if `package-lock.json` installs a different `@playwright/test` release. GitHub Actions mounts the checked-out repository into the job container, so the job needs no separate `docker run` volume mount. The artifact step preserves failure traces after the container exits.

If your tests use a GitHub Actions service container, connect to its service name rather than `localhost` from the Playwright job container. For broader pipeline decisions, see the [Playwright CI/CD guide](/blog/playwright-ci-cd-guide).

## Why screenshots differ between host and container

A screenshot baseline captured on your host can fail a visual-diff test in Docker even when the page has not changed. Playwright compares rendered pixels, and the container’s Linux base may use different fonts or font-rendering settings than your host. GPU availability can also change whether Chromium uses hardware or software rasterization. Those differences can move text edges and change anti-aliasing enough to produce a diff.

Capture and compare baselines inside the same pinned Playwright image you use in CI. Do not update baselines on your host and commit them for container runs. If you change the image tag or install new fonts, regenerate the baselines in the updated container before treating new diffs as regressions.

## Other failure modes engineers hit running Playwright in Docker

Missing system libraries usually surface as a browser launch error in a custom image. Playwright needs Linux packages as well as browser binaries. Install both during the image build with `npx playwright install --with-deps`, or use the official image with a tag that matches the installed Playwright version.

`npx playwright install --with-deps`

Date assertions can fail when the container and your laptop use different timezones or locales. Set the browser’s `timezoneId` and `locale` in Playwright configuration when a test depends on date formatting. If your application reads the container’s timezone, set its `TZ` environment variable too. A browser setting will not change how the application server formats dates.

Slow CI starts often mean the job downloads browsers on every run. The official image already contains browser binaries, so avoid running `npx playwright install` when its image tag matches your Playwright package. With a custom image, install the browsers during the Docker build rather than at test startup.

Browser crashes that appear only under parallel load may come from container memory limits. Each worker launches its own browser processes, so reduce the worker count and rerun the suite before treating the failures as test regressions.

`npx playwright test --workers=1`

If the crashes stop, review the container’s memory limit alongside its worker count. The [parallel testing guide](/blog/complete-guide-to-parallel-testing) covers that tradeoff in more detail.

## When a hosted browser grid costs less than running your own containers

Self-hosted Playwright containers are usually simpler when you already have spare CI capacity and a small, predictable test suite. You control the image and pay for the compute you use. As the suite grows, you also maintain browser versions, size workers for peak parallel runs, and investigate failures caused by container resource limits rather than application regressions.

A hosted grid becomes worth comparing when those operating costs exceed its usage fees. For example, if pull requests queue behind a fixed pool of workers, you can add capacity, but that capacity may sit idle between bursts. Version drift adds another cost when engineers must update images and reproduce failures across local and CI environments. Compare your CI compute bill and maintenance time against hosted usage at your actual peak concurrency, not just the price of one test run.

Momentic runs tests on hosted Chromium browsers, which is one way to avoid maintaining the containers yourself. The [browser infrastructure comparison](/blog/cloud-browser-infrastructure-explained) covers the capacity tradeoff in more detail, while the [headless testing guide](/blog/headless-browser-testing-in-ci) covers CI-specific constraints.

## Takeaway

Running the same pinned Playwright container locally and in CI makes failures easier to reproduce because both runs use the same browser binaries and Linux dependencies. Keep runtime settings and screenshot baselines consistent as well, since the image alone cannot prevent every environment difference. If image maintenance and runner capacity take more engineering time than your test volume justifies, compare that cost with a hosted browser grid.

## Frequently asked questions

Can you run Playwright in Docker without the official image?    Yes. Start with a supported Debian or Ubuntu image, install your project dependencies with npm ci, and run npx playwright install --with-deps to add browser binaries and system libraries. The official image saves you that installation work.    Does the image support ARM and Apple Silicon?    Check your pinned tag with docker manifest inspect , since architecture support depends on the tag you pull. If the tag lacks arm64, Docker can run its amd64 image on Apple Silicon with --platform=linux/amd64, though emulation can slow tests.    How do you debug a headed test in a container?    Run xvfb-run -a npx playwright test --headed inside the container to give Chromium a virtual display. Xvfb does not show the browser on your desktop. If you need to watch the run, configure remote display access or reproduce the test locally with the same pinned image.    How do you cache browser binaries across builds?    With the official image, the browsers are already installed, so reuse the pinned image rather than downloading them during each build. With a custom Dockerfile, install browsers in a layer after npm ci and preserve Docker’s build cache in CI. A temporary build cache mount alone will not put those binaries in the final image.

Still have additional questions?

## Keep reading.

[Resources   Testing iOS and Android apps with Claude Code     Four ways to test iOS and Android apps with Claude Code, from simctl and adb to XCUITest, Espresso, mobile MCP servers and Momentic mobile tests.     Wei-Wei Wu     12 min read](/blog/claude-code-mobile-app-testing)[Resources   UI and end-to-end testing with OpenAI Codex     How far Codex gets on UI and end-to-end tests, and how AGENTS.md instructions plus a test runner over MCP turn its output into checks you can trust.     Wei-Wei Wu     8 min read](/blog/codex-ui-testing-guide)[Resources   Migrating from Appium to Maestro     How to move an Appium suite to Maestro flows, with a worked login example, a concept map and the cases where Appium is still the better fit.     Wei-Wei Wu     6 min read](/blog/migrate-appium-to-maestro)

## Close the feedback loop.

Point Momentic at your app. Free to start, no credit card.

[Try for free](https://app.momentic.ai/signup) [Contact sales](/sales)
