Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a container that contains three aligned pieces: your locked Node/Karma dependencies, a Chrome or Chromium executable, and every shared Linux library that browser build requires. Point Karma’s ChromeHeadless launcher at that executable, run Karma in single-run mode, and start the container with an init process. The Dockerfile below is a safe starting point, but its browser path and operating-system packages must match the base image you choose.

What the image must provide

Karma does not emulate a browser in Node. Its Chrome launcher starts a real Chrome or Chromium process, so the final runtime image must contain:

  • Project test packages: karma, karma-chrome-launcher, your Karma framework adapter, and the framework itself, all recorded in the lockfile.
  • A browser binary: Chrome, Chromium, or the browser downloaded by Puppeteer.
  • Shared libraries: the graphics, font, NSS, X11 and other Linux libraries required by that exact browser build and distribution.
  • Karma configuration: ChromeHeadless, a resolvable executable path, and single-run behavior.
  • Container process handling: an init process so Chrome child processes are reaped when the test exits.

Installing only an executable is insufficient. A missing shared library produces a startup error even when CHROME_BIN points to a valid file.

Choose a browser-image strategy

Strategy What you get Responsibility Best fit
Puppeteer’s published Docker image Chrome for Testing and the dependencies selected by the Puppeteer project Use a current image tag, keep its runtime requirements, and provide the required sandbox capability A team that wants browser setup maintained with the Puppeteer image
Your own Node/Linux base Control over the Node version, distribution and installed packages Install a compatible Chrome/Chromium build, verify all libraries, and maintain them as versions change A CI platform or application that requires a particular base image

Compare the choices by browser-version control, compatibility with your Node and Linux base, sandbox capabilities available in CI, image size and build time, and the effort required to update operating-system libraries. The available guidance does not establish a universal speed, size or reliability winner.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install Karma dependencies reproducibly

Keep browser-test packages in devDependencies. The exact adapter depends on your test framework; for example, a Jasmine project normally has a Jasmine adapter while a Mocha project uses its own adapter. Do not copy a framework package that your project does not use.

npm install --save-dev karma karma-chrome-launcher karma-jasmine jasmine-core
# or install the adapter and framework used by your project

Commit package-lock.json (or the lockfile used by your package manager). The image build should use the lockfile rather than resolving a new dependency graph on every build.

A custom Dockerfile

This example assumes the selected base image already contains Chrome at /usr/bin/google-chrome and its required libraries. If it does not, install a compatible browser and dependencies in the image, or switch to the current Puppeteer image and use its documented executable path.

FROM node:<project-compatible-version>

WORKDIR /app

# Install exactly what the lockfile specifies.
COPY package*.json ./
RUN npm ci

COPY . .

# Change this to the path in the final runtime image.
ENV CHROME_BIN=/usr/bin/google-chrome

CMD ["npm", "test", "--", "--single-run", "--browsers=ChromeHeadless"]

Replace <project-compatible-version> with a maintained Node image compatible with your project. The placeholder is intentional: the correct Node major version is a project decision, not a universal requirement. Likewise, do not leave CHROME_BIN pointing at a path that exists only in a build stage.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Configure Karma for headless Chrome

A minimal configuration enables the launcher and ensures that the process exits after one run.

module.exports = function (config) {
  config.set({
    frameworks: ['jasmine'],
    files: [
      'src/**/*.js',
      'test/**/*.spec.js'
    ],
    reporters: ['progress'],
    browsers: ['ChromeHeadless'],
    singleRun: true,
    autoWatch: false
  });
};

Use the framework, file globs and reporters that your project already needs. singleRun: true and autoWatch: false prevent a CI container from waiting for file changes.

Set the executable explicitly

The Chrome launcher recognizes CHROME_BIN for Chrome and CHROMIUM_BIN for Chromium. Confirm the path inside the built image:

docker run --rm your-karma-image sh -lc 'echo "$CHROME_BIN"; "$CHROME_BIN" --version'

If Puppeteer supplies the browser, set the path before Karma’s configuration is created:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
process.env.CHROME_BIN = require('puppeteer').executablePath();

module.exports = function (config) {
  config.set({
    browsers: ['ChromeHeadless'],
    singleRun: true,
    autoWatch: false
  });
};

This requires Puppeteer and its browser to be installed in the final runtime image, not merely in an earlier build stage.

Build and run the image

  1. Create the image: docker build -t karma-headless .
  2. Run one test session with an init process: docker run --rm --init karma-headless
  3. Inspect the exit status in CI. A successful Karma run should return zero; a failed test or browser startup error should return non-zero.

The --init flag adds a small init process that handles child-process cleanup. Puppeteer specifically recommends an init process or an equivalent custom entry point for containers that launch Chrome.

Sandboxing and CI permissions

Prefer Chrome’s sandbox when the container runtime supports it. Puppeteer’s official image is designed to run sandboxed and requires the container’s SYS_ADMIN capability. A runtime that does not provide that capability may fail with a “no usable sandbox” or similar startup message.

Some documented CI configurations use --no-sandbox, but that is an environment-specific workaround, not a universal Docker flag. Disabling the sandbox removes a browser isolation layer. If your CI leaves no workable alternative, limit the container’s access, avoid treating untrusted pages as harmless, and document the security decision.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
# Prefer the sandboxed arrangement required by your chosen image.
docker run --rm --init --cap-add=SYS_ADMIN karma-headless

Only add the capability when the image and runtime documentation require it. Do not blindly combine --no-sandbox with every Chrome container.

Using Puppeteer’s published image

The published Puppeteer image bundles Chrome for Testing and the dependencies it expects. Follow the current tag and runtime requirements published for that image rather than hard-coding an old tag from an example. Your project files can be copied into that image and tested with the Puppeteer-provided executable path.

This route reduces the amount of browser packaging you maintain, but couples the image to Puppeteer’s Node/Linux choices and its sandbox requirement. A custom base gives more control but makes you responsible for matching browser releases to system libraries.

Make the build smaller and repeatable

  • Copy package manifests before application files so Docker can reuse the dependency layer when source files change.
  • Use npm ci, not an unconstrained install, in CI image builds.
  • Keep test dependencies in the image that actually executes Karma; omitting development dependencies causes missing-launcher errors.
  • Pin compatible versions through the lockfile and update the browser, Puppeteer (if used), Node base and Linux packages as a tested set.
  • Do not remove libraries merely because a browser appears to start once. Fonts, rendering libraries and network security libraries can be needed by particular pages or tests.

Troubleshoot failures systematically

“Chrome executable not found”

Check the final image, not the build host:

docker run --rm karma-headless sh -lc 'command -v google-chrome || command -v chromium || true; ls -l "$CHROME_BIN" 2>/dev/null || true'

Set CHROME_BIN or CHROMIUM_BIN to the actual path. With Puppeteer, use require('puppeteer').executablePath() in karma.conf.js.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Shared-library or browser-startup errors

Identify the Linux distribution and browser build, then install the libraries required by that combination. Dependency lists from an older troubleshooting page may not match a newer Chrome release or a different base image; do not paste them unchanged.

“No usable sandbox”

Verify whether your chosen image expects SYS_ADMIN and whether the CI runtime permits it. Use a sandboxed run where possible. Treat --no-sandbox as a constrained, documented exception rather than a default.

Zombie or lingering Chrome processes

Run with docker run --init, or provide an entry point that supplies an equivalent init process. This allows the container to reap browser child processes when Karma exits.

Karma never exits

Set singleRun: true and autoWatch: false, or pass --single-run on the command line. Make sure no watch-mode script wraps the Docker command.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Headless flags do not fix the root cause

Start with Karma’s built-in ChromeHeadless launcher. Add a custom launcher only for a demonstrated requirement, such as a specific flag or profile. A custom launcher should extend the headless base rather than accumulating copied flags from unrelated CI examples.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to obtain rendered screenshots rather than execute Karma tests, ScreenshotNeo provides a hosted screenshot API and MCP server. One GET request returns PNG, JPEG, WebP or PDF; it handles the browser environment for you.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for parameters and response details. Before capture, it accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

A free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When this container design is the right answer

  • Use a Dockerized Karma runner when you need browser-context tests that exercise the page in Chrome rather than JavaScript solely in Node.
  • Use the Puppeteer image when its browser, Node/Linux base and sandbox requirements fit your CI.
  • Build your own image when controlling the base image is more important than minimizing browser-maintenance work.
  • In every route, verify the browser path, shared libraries, sandbox policy, init process and single-run exit behavior in the final runtime image.

Frequently Asked Questions

Can I run Karma tests with Chromium instead of Google Chrome?

Yes. Install Chromium in the final image and set CHROMIUM_BIN to its actual executable path; keep the ChromeHeadless launcher unless your launcher package documents a different name.

Why does the Dockerfile not install one universal list of Chrome packages?

Required libraries vary by Linux distribution, browser release and base image. A package list copied from another environment can be incomplete or unnecessary, so verify dependencies against the exact image and browser you deploy.

Should every Chrome container use --no-sandbox?

No. Prefer sandboxed Chrome. Use a no-sandbox configuration only when your runtime cannot support the sandbox, and treat the reduced isolation as a security trade-off.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.