Skip to content
Latchkey

How to Run pa11y Accessibility Tests in CI

pa11y runs accessibility checks by loading pages in headless Chrome through Puppeteer - so its CI requirements are the standard headless-Chrome ones plus a URL to test.

pa11y (and pa11y-ci) audits web pages for accessibility issues by loading them in headless Chrome via Puppeteer. In CI it needs the same Chrome flags and system libraries as any Puppeteer job, plus a reachable URL - either a static build served locally or a deployed environment.

Why it fails in CI

pa11y launches Chrome through Puppeteer, so in a container it fails without --no-sandbox, and on a slim runner it fails on missing Chromium shared libraries. It also needs the page to be reachable - testing localhost requires serving your build first.

  • No usable sandbox! - containerized Chrome needs --no-sandbox.
  • error while loading shared libraries: libnss3.so - missing Chromium libs.
  • Timeouts/connection refused - the target URL is not being served.

Install & run it reliably

Pass Chrome args via pa11y’s config, install the Chromium libraries (or use a Puppeteer base image), and serve your build before running pa11y-ci against the URL.

.github/workflows/a11y.yml
npm ci
# serve the built site, then run pa11y-ci against it
npx serve -l 3000 ./dist &
npx wait-on http://localhost:3000

# .pa11yci.json: { "defaults": { "chromeLaunchConfig":
#   { "args": ["--no-sandbox"] } }, "urls": ["http://localhost:3000"] }
npx pa11y-ci

Cache & speed

Cache the Puppeteer browser download (~/.cache/puppeteer) keyed on the Puppeteer version, and ~/.npm for Node deps. Bake the Chromium system libraries into the image to skip the apt step each run.

.github/workflows/ci.yml
- uses: actions/cache@v4
  with:
    path: ~/.cache/puppeteer
    key: puppeteer-${{ hashFiles('**/package-lock.json') }}

Common errors

  • No usable sandbox! → add --no-sandbox to chromeLaunchConfig.args.
  • libnss3.so / shared-library errors → install the Chromium system libs or use a Puppeteer image.
  • Connection refused / timeout → serve your build and wait-on it before pa11y runs.
  • Chrome download blocked → cache ~/.cache/puppeteer or use a base image with Chromium.

Key takeaways

  • pa11y drives headless Chrome via Puppeteer - apply the standard Chrome CI fixes.
  • Add --no-sandbox in chromeLaunchConfig; install Chromium system libs.
  • Serve your build and wait-on it before running pa11y-ci.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →