skip to content

Why must the mcr.microsoft.com/playwright image tag match your installed Playwright version?

level: middleimportance: must knowfreq 62%

answer

  1. Two halves must agree
  2. Browsers ship with the library
  3. Tag names version and base OS
  4. Revision-stamped folders under /ms-playwright
  5. Fails at first launch, not install

basics

~20 s

Each Playwright release pins exact browser builds, and the image ships only those. A mismatched tag leaves the library looking for a browser revision that is not in /ms-playwright, so every test fails at launch.

solid answer

~40 s

Playwright drives browser builds it ships itself, not whatever is installed on the machine, and each release pins an exact revision of Chromium, Firefox and WebKit. The official image is that matched set baked into an OS, and its tag names both halves: `v1.63.0-noble` is the 1.63.0 browsers on an Ubuntu Noble base. The image puts them in `/ms-playwright` and points `PLAYWRIGHT_BROWSERS_PATH` there, in revision-stamped folders. The lookup is exact, so a newer image does not satisfy an older library and vice versa — both directions fail identically, at the first `browserType.launch`, with an executable-not-found message naming a missing revision. Practically: bump the image tag in the same commit as `@playwright/test`, and pin the full version rather than a floating tag.

code

bash · 4 lines
bash
docker run --rm --ipc=host \
  -v "$PWD":/work -w /work \
  mcr.microsoft.com/playwright:v1.63.0-noble \
  sh -c 'npm ci && npx playwright --version && npx playwright test'

go deeper

for a junior

Remember that Playwright brings its own browsers rather than using the ones on the machine, and that the container image tag names the Playwright version those browsers belong to.

for a middle

Explain the mechanics: browsers live in revision-stamped folders under /ms-playwright, the lookup is exact, and both a too-old and a too-new image fail at the first launch.

for a senior

Show you would recognise every-test-fails-in-seconds as version skew rather than an outage, and that you enforce the pin by bumping tag and dependency in one reviewed commit.

for a principal

Own the policy: full version pins over floating tags, upgrades as a single change across repositories, and a story for reproducing an old failing run months later.

## Two version numbers that must agree A Playwright run has two halves: the **library** you install from npm (`@playwright/test`) and the **browser builds** it drives. Playwright never uses whatever Chrome or Firefox already exists on the machine. Every release pins an exact, internally numbered build of Chromium, Firefox and WebKit, patched to speak the automation protocol that release expects. The npm version and the browser revision are a matched pair, shipped and tested together. The official container image is that pair baked into an operating system. Its tag names both halves: `mcr.microsoft.com/playwright:v1.63.0-noble` is the Playwright 1.63.0 browser set on an Ubuntu Noble base. Run `@playwright/test` 1.63 inside it and the halves line up. Run an older or newer line inside it and they do not. ## Where the browsers live inside the image - Browsers are installed to `/ms-playwright`, outside your project directory. - The image sets `PLAYWRIGHT_BROWSERS_PATH=/ms-playwright`, so the library looks there instead of in the per-user cache under the home directory. - Each engine sits in a revision-stamped folder such as `chromium-1187`; that number comes from the Playwright release, not from the browser's public version number. - The image also carries the system libraries those browsers link against, and a non-root `pwuser` account to run as. Because the folder name carries the revision, the lookup is exact. The library asks for the one revision its release pinned; if that folder is absent there is no fallback and no "close enough" match. ## What a mismatch actually does Nothing fails at install time. `npm ci` succeeds, the config loads, the runner starts and collects tests. The failure lands on the first browser launch, once per worker, as `browserType.launch: Executable doesn't exist at /ms-playwright/chromium-<rev>/...` followed by the suggestion to run `npx playwright install`. On a large payroll regression suite that means every test failing identically a few seconds in, which reads like an infrastructure outage rather than a version skew — so people restart runners and rotate credentials before they look at the tag. | Skew | Symptom | Fix | |---|---|---| | Library newer than the image tag | Launch fails; the newer revision is missing from `/ms-playwright` | Bump the image tag to match the library | | Library older than the image tag | Launch fails the same way; the older revision is not there either | Pin the tag to the library's version | | Right version, wrong base image | Launch fails on a missing system library | Use a base the project's version publishes | The middle row surprises people: a *newer* image does not satisfy an older library. This is a lookup by exact revision, not a minimum-version check, so "the image is at least as new" buys you nothing. ## Keeping the two in step 1. Treat the image tag as a mirror of `package.json`. When you bump `@playwright/test`, bump the tag in the same commit — a version bump that touches only one side is an incomplete change. 2. Pin the full version in the tag, `v1.63.0-noble` rather than a floating alias, so rebuilding an old commit does not silently move the browsers under a fixed library. 3. Make the pair visible: printing `npx playwright --version` as the first line of the job puts the skew in the log instead of leaving it buried in a launch stack trace. 4. If you cannot use the image at all, install the browsers explicitly for the version you depend on rather than assuming the base has any. ## Why Playwright is strict here Shipping the browsers with the library is a deliberate trade. Because the pair is fixed, a green run on your laptop and a green run in CI exercised byte-identical browser builds, and there is no compatibility matrix to reason about between an automation library and a separately upgraded browser. The cost lands exactly here: those builds are an artefact you must transport into every environment, and the image tag is the transport mechanism. Version-pinning it is not ceremony — it is the whole reason the image exists rather than a plain Ubuntu base with a browser apt-installed on top. A useful consequence: because the tag pins everything, an image digest recorded in a job log tells you precisely which browser code produced a screenshot diff months later, which is what makes an old failing payroll run reproducible at all.

  • If the image is newer than the library, why does the run still fail?
    Because the lookup is by exact revision, not by minimum version. Playwright 1.62 asks for the revision 1.62 pinned; a 1.63 image contains a different revision folder and nothing else. There is no fallback to the nearest build, so a newer image fails exactly like an older one.
  • What does the suffix after the version in the tag mean?
    It names the Ubuntu base the browsers were built for and linked against, so the tag pins both the Playwright release and the operating system underneath it. That matters because the browser binaries depend on system libraries from that base, which is why the same version is published per base rather than once.
  • How would you stop the two from drifting apart in the first place?
    Make the tag part of the same change as the dependency: bump `@playwright/test` and the image tag in one commit so review sees both. Pin the full patch version rather than a floating tag, and print `npx playwright --version` early in the job so any skew shows up in the log rather than in a launch stack trace.

The library and its browser builds are cut like a lock and its key: a newer key is not a better fit, it simply does not turn.

saying these in an interview costs you the question

  • Thinks Playwright drives the machine's installed Chrome
  • Assumes a newer image satisfies an older library
  • Uses a floating image tag and expects reproducible runs
  • Bumps the npm version without touching the image tag
  • Reads every-test-fails-at-launch as a runner outage
  • Believes npm install downloads the browsers