Choosing a Karma Browser Launcher for CI: ChromeHeadless, FirefoxHeadless, or Puppeteer
Pick the right Karma browser launcher for CI: a comparison of ChromeHeadless, FirefoxHeadless, and Puppeteer, with a switchable karma.conf.js and validation steps.
31 Aug 2026, 21:27 UTC

Your Karma test suite runs fine on a developer laptop but fails on the CI agent because there is no display, no Chrome installed, or the wrong launcher plugin. The fix is not "install a GUI" — it is picking the right launcher for your constraints. This guide compares the three supported options and shows a configuration that lets you switch between them with one environment variable.
The decision and the constraints
Assume a typical CI setup: Linux build agents without a display server, Node.js 14 or newer, and Karma 6.x. You need a launcher that starts quickly (it runs on every pipeline job), executes headlessly, and does not require manual binary installation on each agent. The three realistic choices are karma-chrome-launcher, karma-firefox-launcher, and karma-puppeteer-launcher.
Comparing the supported options
| Launcher | Install | Headless support | Typical startup | Notes |
|---|---|---|---|---|
| karma-chrome-launcher | npm i -D karma-chrome-launcher | Yes, via ChromeHeadless | ~2s | Requires a Chrome or Chromium binary on the agent |
| karma-firefox-launcher | npm i -D karma-firefox-launcher | Yes, via FirefoxHeadless | ~3s | Needs Firefox 57+; useful for Gecko-specific behavior |
| karma-puppeteer-launcher | npm i -D karma-puppeteer-launcher | Yes, built in | ~1.5s | Downloads its own Chromium (~100MB); no system browser needed |
Trade-offs in practice
ChromeHeadless gives the best compatibility with modern web platform features and lets you debug locally with the same browser your users likely run. The cost is operational: every CI image must contain a Chrome or Chromium binary, and the launcher version must be recent — versions of karma-chrome-launcher older than 3.x may not handle the modern headless flag correctly and can fail against current Chrome releases.
FirefoxHeadless is the option when you want coverage of Gecko-specific rendering or API differences, or when Chrome licensing is a concern in your environment. Startup is slightly slower, so it is usually a second launcher rather than the default.
Puppeteer removes the external binary dependency entirely — the npm install pulls a matching Chromium build, so any agent with Node can run tests immediately. It also tends to start fastest. The downsides: roughly 100MB added to your install or CI cache, the bundled Chromium may trail the latest stable Chrome features, and Chromium in containers is sensitive to a small /dev/shm. On Docker-based agents, either mount a larger shm (--shm-size=1g) or pass the --no-sandbox --disable-dev-shm-usage flags via a custom launcher, or large suites can crash with opaque browser-disconnect errors.
Implementation: one config, switchable launcher
Install all three launchers as dev dependencies (or just the ones you use), then select via an environment variable. Run this in your project root; no elevated permissions needed.
// karma.conf.js
const launchers = {
chrome: { browsers: ['ChromeHeadless'] },
firefox: { browsers: ['FirefoxHeadless'] },
puppeteer:{ browsers: ['ChromeHeadless'] } // puppeteer launcher registers this name
};
module.exports = function (config) {
const choice = process.env.CI_BROWSER || 'puppeteer';
config.set({
frameworks: ['jasmine'],
files: ['src/**/*.spec.js'],
singleRun: true,
autoWatch: false,
reporters: ['progress', 'coverage'],
...launchers[choice]
});
};
In a Dockerfile-based agent using the system-Chrome option, install the binary explicitly:
RUN apt-get update && apt-get install -y chromium \
&& rm -rf /var/lib/apt/lists/*
ENV CHROME_BIN=/usr/bin/chromium
The CHROME_BIN variable matters: karma-chrome-launcher locates the browser through it when Chrome is not in a default path. With the Puppeteer launcher this step disappears entirely.
Validating the choice
Three quick checks, all run from the project root:
- Confirm the plugin version:
npm list karma-chrome-launcher(substitute your launcher). Verify it satisfies Karma 6 compatibility. - Run
CI_BROWSER=chrome npx karma start karma.conf.js --single-runand read the startup log — the browser name line should showHeadlessChrome,HeadlessFirefox, or the Puppeteer-provided Chromium. A hang or "Cannot start browser" error almost always means a missing binary or wrongCHROME_BIN. - Compare total CI job duration before and after switching. If you moved from system Chrome to Puppeteer and the job got slower, check your npm cache — re-downloading Chromium on every run erases the startup advantage.
Limitations
Startup times above are rough orders of magnitude, not guarantees — measure on your own agents. Launcher plugin versions and browser compatibility move over time, so re-verify the minimum plugin version against your Karma release before pinning. If your suite needs real GPU rendering or WebGL, none of the headless defaults may suffice; that is a separate decision involving headed browsers under Xvfb.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.