Playwright's Chromium crashes pages only when the suite runs inside a container — what does `--ipc=host` change?
answer
- Only one engine misbehaves
- Containers get a private, tiny tmpfs
- Renderer processes need shared memory
- Host IPC namespace removes the ceiling
- Runtime flag, not a config option
basics
~10 sContainers get a private, small /dev/shm, and Chromium passes large buffers through that shared memory. When it fills, renderers die and pages crash. Running with --ipc=host gives the container the host's shared memory instead.
solid answer
~40 sChromium is multi-process and moves rendered frames and bitmaps between its processes through POSIX shared memory backed by `/dev/shm`. A container runtime gives each container a private IPC namespace with a small `/dev/shm` — historically 64 MB — so a heavy page exhausts it, the renderer process dies, and Playwright reports the page's target as crashed. It looks like flake: it worsens with more workers and bigger pages, and only Chromium projects are affected. `--ipc=host` puts the container in the host's IPC namespace so it uses the host's shared memory, which is the configuration Playwright documents for its image. Alternatives are a larger `--shm-size`, which still guesses a number, or Chromium's `--disable-dev-shm-usage`, which trades memory for disk. It is a container-runtime flag; no Playwright config replaces it.
code
bash · 4 linesdocker run --rm --ipc=host \
-v "$PWD":/work -w /work \
mcr.microsoft.com/playwright:v1.63.0-noble \
npx playwright test --project=chromiumgo deeper
Know that running browsers in containers needs extra care, and that the Playwright image is normally started with --ipc=host so Chromium has enough shared memory.
Explain the mechanism: Chromium moves buffers through /dev/shm, containers get a small private one, and exhausting it kills the renderer behind the page.
Demonstrate the diagnosis — one engine failing under load, a 64 MB tmpfs inside the job — and fix it at the runtime before touching tests, timeouts or retries.
Weigh the isolation you give up against the flake you remove, decide the standard for shared runners, and make sure the reason is recorded where the flag is set.
## The symptom The suite is green on laptops and green on a self-hosted runner, then moves into a container and starts losing pages. Chromium tests fail with crashed-target errors, or a page simply stops responding mid-test; Firefox and WebKit in the same run are fine. It gets worse with more workers, bigger pages and longer tests — a payroll regression suite rendering wide report grids is a textbook trigger — and it looks like flake because the same test passes on a rerun. ## Why `/dev/shm` is the culprit Chromium is multi-process. The browser process, each renderer and the GPU process pass large buffers (rendered frames, bitmaps, decoded media) through **POSIX shared memory**, which on Linux is backed by the `/dev/shm` tmpfs. A normal Linux host sizes that generously. A container gets its own tiny default — historically 64 MB — because the runtime creates a private IPC namespace and a small `/dev/shm` mount for it. When Chromium asks for shared memory and the tmpfs is full, the allocation fails and the renderer process dies. From Playwright's side that surfaces as the page's target crashing, so every pending action and assertion against it fails at once. ## What `--ipc=host` changes Running the container with `--ipc=host` puts it in the **host's IPC namespace** and gives it the host's `/dev/shm` rather than the cramped per-container one. Chromium's shared-memory allocations then have the room they would have had on a normal machine, and the crashes stop. This is the configuration Playwright's own documentation recommends for running its image, which is why it appears in nearly every working example of `docker run` with this image. - It is a **runtime** flag on the container, not a Playwright option — no config change makes up for its absence. - It only matters for Chromium (and Chromium-channel projects such as Chrome and Edge); the other engines do not use `/dev/shm` this way. - It is orthogonal to the image tag: the right image with the wrong IPC setting still crashes. ## The alternatives, and what each costs | Approach | Effect | Cost | |---|---|---| | `--ipc=host` | Container shares the host IPC namespace and its `/dev/shm` | Drops an isolation boundary between container and host | | Larger `--shm-size` | Keeps the private namespace, just enlarges the tmpfs | You have to guess a number; a heavier page can still exhaust it | | Chromium's `--disable-dev-shm-usage` | Chromium writes shared memory to `/tmp` instead | Disk-backed rather than memory-backed, so slower; not the browser's normal path | For a CI runner that already boots per job, the isolation cost of `--ipc=host` is usually trivial and it is the recommended default. On a shared multi-tenant host where dropping the namespace boundary is unacceptable, a generous `--shm-size` is the honest fallback — but it is a guess, and the failure mode when the guess is wrong is exactly the flake you were trying to remove. ## Diagnosing it before guessing 1. Note that only Chromium projects fail while Firefox and WebKit pass in the same run — that asymmetry alone points at shared memory rather than at the image or the app. 2. Check whether failures cluster under load: more workers or more parallel pages making it worse is the signature of a fixed-size resource being exhausted. 3. Look at the container's `/dev/shm` size from inside the job; a 64 MB tmpfs is the tell. 4. Add `--ipc=host` and re-run the same commit before changing any test code. If the crashes vanish, you have your answer and you have not touched the suite. ## Why it is worth knowing rather than copying The flag is easy to copy from a sample command and impossible to justify later. Teams that copied it tend to strip it when they "clean up" the container invocation, or lose it when they move from a plain `docker run` to running the suite inside a job-level container, and the flake returns weeks later under a new name. Knowing that it exists to give Chromium real shared memory is what stops that regression — and it also tells you the fix does **not** belong in retries, timeouts or the test code, which is where teams usually spend the first week.
- Why do Firefox and WebKit projects survive the same container?They do not route large inter-process buffers through /dev/shm the way Chromium's multi-process rendering does, so the small tmpfs never becomes their bottleneck. That asymmetry is the cheapest diagnostic you have: if one engine crashes under load while the others pass in the same run, suspect shared memory before the app.
- When would you prefer a larger --shm-size over --ipc=host?On a shared multi-tenant host where dropping the IPC namespace boundary is unacceptable. You keep the isolation and enlarge the tmpfs instead, accepting that the size is a guess: a heavier page can still exhaust it, and the failure mode when it does is exactly the flake you were removing.
- How do you keep the flag from being lost during a CI refactor?Write down why it exists next to where it is set, and keep a Chromium-heavy test in the suite that fails fast without it. Flags copied from a sample get stripped in cleanups; a one-line reason and a test that notices are what stop the flake returning weeks later under a new name.
Chromium's processes hand each other large trays through one small hatch; a container shrinks the hatch, so under load the trays stop fitting and the worker on the other side gives up.
saying these in an interview costs you the question
- Blames the application or adds retries instead
- Thinks it is a Playwright config option
- Believes --no-sandbox fixes the crashes
- Assumes the right image tag removes the need
- Treats crashed targets as ordinary test flake
- Sets it without knowing it drops an isolation boundary