Running webdriverio tests using headless chrome in Docker

Using headless chrome for your UI tests works great out of the box on your laptop, but it won’t work out of the box when you're trying to run your tests in Docker. One recent work project was getting webdriverio tests successfully running in a Docker container as part of a Jenkins pipeline. Locally, our tests worked fine, but when we ran them in docker, they became super flaky with errors showing random Chrome crashes, or “Chrome is unreachable” errors. Hope is not lost though - There is in fact a proper incantation of options to pass to Chrome to get it running successfully (and without random crashes).

tl;dr Here's a snippet of wdio.conf.js showing our Chrome configuration. To see why some of these need to be included, read on.

// wdio.conf.js
...
browserName: 'chrome',
chromeOptions: {
  args: [
    '--disable-infobars',
    '--window-size=1280,800',
  ].concat((function() {
    return process.env.HEADLESS_CHROME === '1' ? [
      '--headless',
      '--no-sandbox',
      '--disable-gpu',
      '--disable-setuid-sandbox',
      '--disable-dev-shm-usage'] : [];
  })()),
...

--disable-dev-shm-usage

Add this flag if you're seeing the error (like this guy did) “Session deleted because of page crash from tab crash” in your logs when your tests fail (or a message along the lines of a page crash), or Chrome is unreachable.

Why do we need this?

There’s a tmp folder that Chrome uses related to its internal memory management that by default tries to use the /dev/shm directory. I don't quite understand the internals of Chrome, but here's some details about this from the puppeteer project. Anyway, Docker containers have a default size of 64MB for /dev/shm (cmd+f for --shm-size). Chrome quickly runs out of space in there and your page will then crash, taking your tests down with it. No bueno. There’s two solutions to this:

The easiest way to deal with this problem is to just not use it. There are some small cons, but a good rule of thumb is that unless you're trying to parallelize multiple concurrent Chrome sessions within one container, you're probably safe to disable it by adding --disable-dev-shm-usage to your Chrome options.

If you are though (like this engineer), then you'll need to increase the size of your /dev/shm. For Docker containers,

  • there’s a flag for increasing the shared memory size --shm-size=1G that you can pass to your docker run command.
  • you can also mount /dev/shm from your host machine to the docker container via -v /dev/shm:/dev/shm

--disable-setuid-sandbox --no-sandbox

If you see errors failing to connect to Chrome like this, you might need these flags. Chrome uses sandboxing in order to protect your machine from random content on the internet (see details here). If you’re running Chrome inside of a disposable container, then your container is already acting as a sandbox for Chrome, and so you don’t quite need chrome’s sandbox (your mileage may vary here depending on what you’re trying to do).

I typically see these two flags passed along together in various writeups online. This answer from the Chromium team suggests that --disable-setuid-sandbox was only used in older versions of chrome.

--disable-gpu

The official Puppeteer docs suggest that this flag is only needed on Windows. Various snippets online usually use --disable-gpu alongside --headless because an earlier version of headless chrome had bugs with it.

--disable-infobars

This flag can help a minor annoyance - if you take a screenshot of the browser in your tests, then you’ll see a bar at the top of your browser page saying that Chrome is being controlled by automated process. This also pushes elements farther down the page. Its usually fine, but if it gets in your way, then try using this option. This flag was at one point removed around Jan 2018, and then added back.

Wrapping up

Yes, Chrome can run headlessly and it can run inside of a container, and be stable (for the most part). You do however need the right concoction of parameters to get it working right, but hopefully this saves you a few hours of debugging random page crashes. Try out those Chrome options in your configuration (they will also work if you're using puppeteer or other selenium/chrome combo).

Running into different errors? See how to attach the VSCode debugger to step through your UI tests to help you narrow down the issue.

You might be interested in…

Menu