Skip to navigation

Docker and headless

Continuous integration runners are usually headless and often containerized. This guide covers running Doc Detective with the official Docker image, what changes when no display is available, and how to record video headlessly.

Before you begin

  • Install Docker if you plan to use the container image.
  • Have a spec or input directory you can run.

Step 1: Run the official container

The official image is docdetective/docdetective:linux on Docker Hub. Its entrypoint is the Doc Detective CLI, so anything you pass after the image name goes straight to doc-detective. Mount your project so the container can read your tests and write results back out:

docker run -v .:/app docdetective/docdetective:linux --input /app/api.spec.json

The -v .:/app flag mounts your current directory to /app inside the container. Because the working directory is /app, point --input and --output at paths under /app so results and artifacts land back in your project.

Inside the official image, unsafe steps run automatically without --allow-unsafe. Those include runCode snippets detected from Markdown code fences. The image identifies itself as a container, and Doc Detective allows unsafe steps in that environment by default. To opt out, set allowUnsafeSteps to false in your config.

Step 2: Understand headless behavior

A headless environment has no display, which affects browser-driven tests:

  • Browser tests run headless automatically when no display is available. On Ubuntu CI runners, Chrome and Firefox contexts fall back to headless mode.
  • Video recording is the main thing that needs a display. The default browser recording engine captures the Chrome viewport and works headless; the ffmpeg engine captures the screen and needs a real or virtual display.

Some tests record video with the ffmpeg engine on a headless runner. Run those on a macOS or Windows runner instead, where a display is available. For engine details, see Record video.

Step 3: Warm runtime assets

Doc Detective installs heavy assets on first use, including browsers, drivers, and npm packages. In CI, install them ahead of time and cache them so each run starts fast.

Point the cache at a workspace path with DOC_DETECTIVE_CACHE_DIR, then pre-install everything with doc-detective install all:

export DOC_DETECTIVE_CACHE_DIR="$PWD/.doc-detective-cache"
doc-detective install all --yes

On a fresh npm install, Doc Detective also pre-warms these assets automatically. To skip that step, set DOC_DETECTIVE_AUTOINSTALL=0. That’s useful when you want to control exactly when assets download in CI. The official container ships with assets already warmed, so you don’t need this step inside it.

Each run front-loads whatever the cache is missing. Before executing tests, Doc Detective plans the installs, device boots, and downloads its tests need, then runs them concurrently as a warm phase. With a persisted cache the phase completes in milliseconds. On a cold cache it overlaps the downloads instead of paying them one test at a time. The per-task timings land in the JSON results. See Read the warm-phase timings.

docdetective/docdetective:linux is a multi-arch image covering linux/amd64 and linux/arm64. Chrome and ChromeDriver have no native linux/arm64 build upstream, because Google’s Chrome for Testing only publishes x64 binaries. On the linux/arm64 variant, Chrome and ChromeDriver pre-warming is skipped rather than failing the image. To run Chrome-driven tests on arm64 hosts, run the linux/amd64 variant under emulation, or use Firefox instead. Firefox and GeckoDriver both ship in the image for arm64 and run natively, so a Firefox context needs no extra setup.

For a GitHub Actions caching recipe, see Runtime caching.

Step 4: Run headless in CI

Put the pieces together in a generic, platform-agnostic pipeline step. Because the CLI doesn’t fail on test failures on its own, gate the build explicitly (see Reporters and artifacts):

docker run -v .:/app \
-e DOC_DETECTIVE_AUTOINSTALL=0 \
docdetective/docdetective:linux \
--input /app/docs --reporters json --output /app/results.json

Verify it works

  • The container run prints a terminal summary and writes results to the mounted output path.
  • Browser tests complete without a display error.
  • If you pre-warmed assets, the run starts without downloading browsers or drivers.

Troubleshooting

A recording step is skipped or fails on a headless runner

Cause: The ffmpeg recording engine needs a display, which a headless Linux runner doesn’t have.

Solution: Use the browser recording engine for headless captures, or move recording tests to a macOS or Windows runner. See Record video.

Runs are slow because assets download every time

Cause: The runtime cache isn’t persisted between runs.

Solution: Set DOC_DETECTIVE_CACHE_DIR to a path your CI caches, and pre-install with doc-detective install all --yes. See Runtime caching.

What’s next