Playwright is a test runner that uses real browsers
to test web applications (an alternative to tools like
Selenium). By default, Playwright runs these
browsers in headless mode, which means the pages are loaded and tested without
opening the browser window. This is great when running entire test suites
locally, and in CI where having a bunch of rapidly opening windows would be
disconcerting. However, when it comes to writing or debugging individual
tests, it is convenient to open the browser in headed mode to actually see the
page being tested.
Playwright includes the Playwright
Inspector to
conveniently launch the browser in headed mode and a separate window to
control test execution. The inspector is launched by setting PWDEBUG=1
before
calling playwright
, and is compatible with all browsers supported by
Playwright (Safari, Chrome, and Firefox) in all major operating systems.
Playwright also includes a playwright open <url>
subcommand to quickly launch
the inspector on any URL. Examples in this post use playwright open <url>
, but
PWDEBUG=1
is equivalent.
Playwright also provides a convenient Docker
image, which includes all three browsers
pre-installed and configured so you can skip the dependency installation steps.
But what happens if you try to run the Playwright Inspector inside a Docker
container – which normally doesn’t have a graphical user interface?
docker run --rm mcr.microsoft.com/playwright:v1.28.0 npx -y playwright open google.com
╔════════════════════════════════════════════════════════════════════════════════════════════════╗
║ Looks like you launched a headed browser without having a XServer running. ║
║ Set either 'headless: true' or use 'xvfb-run <your-playwright-app>' before running Playwright. ║
║ ║
║ <3 Playwright Team ║
╚════════════════════════════════════════════════════════════════════════════════════════════════╝
No browser window is launched! Instead we get an error message about not “having
a XServer running.” The definition and functionality of XServer are beyond the
scope of this article, but without it we can’t interact with applications that
require a user interface (like the browser). Here’s a more detailed
explanation if you want to learn more.
Searching for this error on the web will return results explaining how to
install and start XServer. That advice applies to non-containerized, Linux-based
systems. If your host system is macOS or Windows you actually don’t want to do
that. Instead we want the container to use the host XServer to launch
Playwright inside the container, which requires two modifications to our
docker
command:
- Set the
DISPLAY
environment variable inside the container using the -e
option
- Mount the XServer Unix socket inside the container using the
-v
option
docker run --rm \
-e DISPLAY=<host display> \
-v /tmp/.X11-unix:/tmp/.X11-unix \
mcr.microsoft.com/playwright:v1.28.0 npx -y playwright open google.com
The value of <host display>
will depend on your host operating system, and you
will need to ensure /tmp/.X11-unix
is available for mounting. The following
sections explain how to do this for Windows and macOS.
Microsoft Windows
You might find it surprising (I certainly did) that Microsoft Windows has a
native XServer even though it’s not a GNU/Linux system. It’s called
WSLg, and it’s included as part of
the Windows Subsystem for
Linux (WSL). You most
likely already have WSL and WSLg installed if you are running Docker
Desktop in recent builds of
Windows 10 and 11.
Let’s start by verifying that WSL and WSLg are installed and running. First,
launch “WSL” from your Start Menu. A Linux terminal window should open (most
likely a recent version of Ubuntu). In that window, verify that the directory
/mnt/wslg/
exists and contains these files inside the Linux filesystem:
ls -a -w 1 /mnt/wslg
.
..
.X11-unix
PulseAudioRDPSink
PulseAudioRDPSource
PulseServer
distro
doc
dumps
pulseaudio.log
runtime-dir
stderr.log
versions.txt
weston.log
wlog.log
If you don’t see “WSL” in your Start Menu, or the ls
command above fails with
No such file or directory
, then your system is missing WSL entirely or is
running an old version. Visit the Microsoft
Store to download an
up-to-date version.
Once you are all set up, we can set DISPLAY=:0
as explained in the official
guide:
docker run --rm \
-e DISPLAY=:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
mcr.microsoft.com/playwright:v1.28.0 npx -y playwright open google.com
If all goes well, that should open two windows: a browser window with Google
loaded, and a Playwright Inspector window. Closing both will also stop the container.
macOS
Apple’s operating system doesn’t include a built-in XServer, but we can use
XQuartz to provide one:
- Install XQuartz:
brew install --cask xquartz
- Open XQuartz, go to Preferences -> Security, and check “Allow connections
from network clients”
- Restart your computer (restarting XQuartz might not be enough)
- Start XQuartz with
xhost +localhost
- Open Docker Desktop and edit settings to give access to
/tmp/.X11-unix
in
Preferences -> Resources -> File sharing
Once XQuartz is running with the right permissions, you can populate the
environment variable and socket:
docker run --rm \
-e DISPLAY=host.docker.internal:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
mcr.microsoft.com/playwright:v1.28.0 npx -y playwright open google.com
If all goes well, that should open two windows: a browser window with Google
loaded, and a Playwright Inspector window. Closing both will also stop the container.
Persisting the configuration
To avoid having to specify the flags every time, you can use a
docker-compose.yml
file to set the environment
variable
and mount the socket.
version: '3'
services:
web:
image: mcr.microsoft.com/playwright:v1.28.0
environment:
- DISPLAY=...
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
Remember: environment
needs DISPLAY=:0
in Windows and
DISPLAY=host.docker.internal:0
in macOS. After editing and saving the file,
run docker-compose
to open the browser (or run any Playwright command) along
with the Playwright Inspector:
docker-compose run web npx -y playwright open google.com
Enjoy using and inspecting the browser from inside containers!