Run Vibium Tests in CircleCI
Run Vibium tests in CircleCI: a config.yml that installs vibium, runs headless, caches Chrome, splits tests in parallel, and stores failure artifacts.
To run Vibium tests in CircleCI, add a .circleci/config.yml that installs vibium with pip or npm, runs your suite headless, caches the auto-downloaded Chrome, and stores failure screenshots as artifacts. Vibium ships as a single Go binary that downloads its own Chrome for Testing on first run, so a CircleCI job has no driver to install and no browser version to align — the classic cause of red pipelines in older stacks. On a cimg/python or cimg/node image you install Chrome's shared system libraries (libnss3, libgbm1, libasound2), install Vibium, set HEADLESS=true, and run your tests. Because Vibium's find() auto-waits for actionability, the same suite that passes on your laptop passes on a slower CircleCI container without sprinkled sleep() calls. Add store_artifacts for failure screenshots and store_test_results for JUnit XML, and you get a fast, debuggable pipeline that runs on every push. This guide gives you copy-paste configs for Python and JavaScript, caching, parallelism, and scheduled runs.
What does a minimal CircleCI config for Vibium look like?
The smallest useful pipeline checks out your code, installs Chrome's libraries and Vibium, then runs the suite headless in one job. Drop this at .circleci/config.yml in your repo root and CircleCI picks it up automatically.
version: 2.1
jobs:
test:
docker:
- image: cimg/python:3.12
environment:
HEADLESS: "true"
steps:
- checkout
- run:
name: Install Chrome system libraries
command: |
sudo apt-get update
sudo apt-get install -y \
libnss3 libatk-bridge2.0-0 libgbm1 libasound2 \
libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 \
libxrandr2 libdrm2 libpango-1.0-0 libcairo2
- run:
name: Install Vibium
command: |
pip install vibium pytest
vibium install # pre-download Chrome for Testing
- run:
name: Run tests
command: pytest -v
workflows:
build-and-test:
jobs:
- testThe HEADLESS: "true" environment variable is read by your launch code (shown below) so Chrome runs without a display. The vibium install step pre-fetches Chrome during setup so the first test does not pay the download cost. That is the entire pipeline — no Selenium Grid, no chromedriver download, no Xvfb.
How does the Vibium CircleCI pipeline flow end to end?
At a high level, every CircleCI job walks the same five stages, from a clean container to stored artifacts. Keeping this shape in mind makes the longer configs below easy to read.
How a Vibium job runs in CircleCI
Each stage maps to a block in config.yml: checkout pulls your code, the install run steps add Chrome libs and Vibium, restore_cache/save_cache handle the browser download, your test run executes the suite with HEADLESS=true, and store_artifacts plus store_test_results capture screenshots and JUnit XML. The rest of this article fleshes out each stage with production-ready snippets.
Why is there no driver or browser to install?
Vibium bundles its WebDriver BiDi engine into one Go binary and downloads a known-good Chrome for Testing itself, so there is nothing to version-match in CI. In older stacks, pipelines broke constantly because the installed browser drifted out of sync with the driver — a Chrome auto-update on the runner would silently break chromedriver. Vibium removes that entire class of failure. The only host setup is Chrome's shared libraries, which the base image mostly provides, and modern headless Chrome renders with no X display, so you never need Xvfb.
This is the single biggest difference between a Vibium pipeline and a legacy Selenium one. See Vibium vs Selenium for the full architectural comparison, or what is Vibium for the fundamentals.
How does my code know to run headless in CircleCI?
Read headless mode from an environment variable in your launch code so the identical suite runs headed locally and headless in CI. This is the single switch the config.yml above flips with HEADLESS: "true".
import os
from vibium import browser_sync as browser
headless = os.getenv("HEADLESS", "false") == "true"
vibe = browser.launch(headless=headless)With this pattern you debug headed on your laptop, push, and the same code runs invisibly on the CircleCI container — there is no separate CI code path to drift out of sync. The JavaScript equivalent reads the same variable:
const { browser } = require('vibium/sync')
const headless = process.env.HEADLESS === 'true'
const bro = browser.launch({ headless })Centralizing this in a fixture or helper (see structuring a Vibium suite) keeps every test consistent and means the CI switch lives in exactly one place.
How do I run Vibium tests in CircleCI with JavaScript or TypeScript?
For a Node project, swap the Python image for cimg/node and install Vibium with npm — everything else about the pipeline is identical. Vibium provides both Python and JavaScript/TypeScript clients from the same Go engine, so your CI shape does not change with the language.
version: 2.1
jobs:
test:
docker:
- image: cimg/node:20.11
environment:
HEADLESS: "true"
steps:
- checkout
- run:
name: Install Chrome system libraries
command: |
sudo apt-get update
sudo apt-get install -y \
libnss3 libatk-bridge2.0-0 libgbm1 libasound2 \
libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 \
libxrandr2 libdrm2 libpango-1.0-0 libcairo2
- run:
name: Install dependencies
command: npm ci
- run:
name: Run tests
command: npm test
workflows:
build-and-test:
jobs:
- testYour package.json lists vibium as a dependency, so npm ci installs it and downloads Chrome for Testing. A test that reads text on a page looks the same in CI as it does locally:
const { browser } = require('vibium/sync')
const bro = browser.launch({ headless: process.env.HEADLESS === 'true' })
const page = bro.page()
page.go('https://example.com')
const heading = page.find('h1')
console.log('Heading:', heading.text())
bro.close()The auto-waiting behaviour of find() — Vibium waits for the element to be present and actionable before returning — is what keeps this stable on a container that is slower and more loaded than your dev machine. There is no wait_for boilerplate to add. See finding elements for the selector and waiting details.
How do I capture screenshots and traces on failure?
Since CircleCI runs are headless and remote, a failure screenshot is the fastest way to see what the page actually showed. Save a PNG when a test fails, write it to a dedicated directory, and let CircleCI upload it with store_artifacts. Vibium's screenshot() returns PNG bytes, so writing it to disk is one line.
In pytest, a fixture that reacts to the test outcome handles this cleanly:
import os
import pytest
from vibium import browser_sync as browser
ARTIFACTS = os.getenv("ARTIFACTS_DIR", "artifacts")
@pytest.fixture
def vibe(request):
os.makedirs(ARTIFACTS, exist_ok=True)
instance = browser.launch(headless=True)
yield instance
if request.node.rep_call.failed:
png = instance.screenshot(full_page=True)
path = os.path.join(ARTIFACTS, f"fail-{request.node.name}.png")
with open(path, "wb") as f:
f.write(png)
instance.quit()Then add the upload steps to your job so the artifacts survive the run:
- run:
name: Run tests
command: pytest -v --junitxml=artifacts/junit.xml
environment:
ARTIFACTS_DIR: artifacts
- store_artifacts:
path: artifacts
destination: vibium
- store_test_results:
path: artifactsThe store_test_results step tells CircleCI to parse the JUnit XML so the web UI shows individual test results and flakiness trends. The store_artifacts step makes the failure PNGs downloadable from the job page.
For richer debugging, record a Vibium trace with screenshots and DOM snapshots, write the resulting trace.zip into the same artifacts/ directory, and open it in the Vibium Trace viewer to scrub the timeline frame by frame. See taking a screenshot for the full capture options and flake-free tests for the debugging workflow.
How do I cache Chrome to speed up CircleCI runs?
Vibium downloads Chrome for Testing once and reuses it, so caching that download removes several seconds from every run. Cache Vibium's browser directory between jobs with save_cache and restore_cache, keyed on your Vibium version, and the vibium install step becomes a near-instant cache hit.
On the CircleCI Linux images, Vibium stores its downloaded browser under the user cache directory. Restore it before installing and save it after:
- restore_cache:
keys:
- vibium-chrome-v1-{{ checksum "requirements.txt" }}
- vibium-chrome-v1-
- run:
name: Install Vibium
command: |
pip install vibium pytest
vibium install
- save_cache:
key: vibium-chrome-v1-{{ checksum "requirements.txt" }}
paths:
- ~/.cache/vibiumKeying the cache on requirements.txt (or package-lock.json for Node) means a fresh cache is built whenever you bump the Vibium version, so you never run against a stale browser. Also cache your language dependencies — pip's wheel cache or Node's node_modules — with the same pattern to shave the install step. On a busy monorepo where the pipeline fires on every commit, these two caches together are the difference between a 90-second and a 30-second setup.
How do I run Vibium tests in parallel across CircleCI containers?
For larger suites, split your tests across multiple containers with CircleCI's parallelism and circleci tests split, and total wall-clock time drops roughly in proportion to the container count. Each container is a fully isolated Chrome instance, which suits Vibium well because every browser.launch() is independent.
jobs:
test:
docker:
- image: cimg/python:3.12
parallelism: 4
environment:
HEADLESS: "true"
steps:
- checkout
# ... install Chrome libs + Vibium as above ...
- run:
name: Run tests in parallel
command: |
mkdir -p artifacts
TESTFILES=$(circleci tests glob "tests/**/test_*.py" | circleci tests split --split-by=timings)
pytest -v --junitxml=artifacts/junit.xml $TESTFILES
- store_test_results:
path: artifacts
- store_artifacts:
path: artifacts
destination: vibiumThe --split-by=timings flag uses the JUnit results from store_test_results on previous runs to balance the split, so each container finishes at roughly the same time instead of one straggler holding up the pipeline. For the broader strategy — process isolation, resource sizing, and shared state — see parallelizing Vibium tests.
A quick decision guide for how much parallelism to use:
| Suite size | Recommended parallelism | Notes |
|---|---|---|
| Under ~30 tests | 1 | Setup and container spin-up cost outweighs the split. |
| ~30–150 tests | 2–4 | The sweet spot for most projects; near-linear speedup. |
| ~150–500 tests | 4–8 | Split by timings so containers stay balanced. |
| 500+ tests | 8+ | Watch your CircleCI credit budget; combine with caching. |
Bump parallelism gradually and watch the per-container run time — once containers spend more time on setup than on tests, you have gone too far and are burning credits for little gain.
What CircleCI resource class should I use for Vibium?
Choose a resource class large enough that headless Chrome does not starve for memory, because an under-provisioned container is a leading cause of intermittent crashes. Each Vibium browser.launch() starts a real Chrome for Testing, and Chrome is memory-hungry — a single tab loading a modern web app can use several hundred megabytes. On the default medium Docker resource class you get roughly 2 vCPUs and 4 GB of RAM, which comfortably runs one browser at a time.
Set the class explicitly with the resource_class key so your runs are predictable:
jobs:
test:
docker:
- image: cimg/python:3.12
resource_class: medium
environment:
HEADLESS: "true"
steps:
- checkout
# ...The right size depends on how many browsers a single container runs at once. If you keep one browser per container and rely on parallelism to scale out, medium is usually enough. If instead you run several browsers inside one container — for example, spawning workers with pytest-xdist — step up to medium+ or large so each Chrome has headroom. The heuristic below keeps you out of out-of-memory territory:
| Browsers per container | Resource class | Approx. RAM | Fits |
|---|---|---|---|
| 1 | medium | ~4 GB | Most suites; one browser per container. |
| 2–3 | medium+ | ~6 GB | In-container concurrency with xdist. |
| 4–6 | large | ~8 GB | Heavy in-container fan-out. |
| 6+ | xlarge | ~16 GB | Rare; usually better to add parallelism. |
As a rule, prefer more containers at a smaller class over one giant container: CircleCI bills by class-minutes, and horizontal parallelism also shortens wall-clock time. Reach for a bigger class only when a workload genuinely needs many browsers sharing state within one job. If you see Chrome crash with no error in your test log, memory pressure is the first thing to rule out — bump the class one step and rerun.
Should I use the Docker executor or the machine executor?
Use the Docker executor for almost all Vibium pipelines, and reserve the machine executor for the rare case that needs a full VM. The Docker executor spins up fast, is cheaper in credits, and — because Vibium downloads its own Chrome for Testing — it needs no special image. The plain cimg/python or cimg/node image plus Chrome's shared libraries is all the environment Vibium requires, which plays perfectly to Docker's strengths.
The machine executor gives you a full Linux VM with a real kernel and Docker daemon, at the cost of slower startup and more credits per minute. You only need it when your tests must do something the Docker executor sandbox restricts — for instance, spinning up your application under test with docker compose in the same job, or exercising kernel-level networking. Even then, Vibium itself does not require the VM; it is your surrounding stack that does.
| Factor | Docker executor | Machine executor |
|---|---|---|
| Startup speed | Fast (seconds) | Slower (VM boot) |
| Credit cost | Lower | Higher |
| Vibium setup | cimg image + libs | Same, plus VM overhead |
| Best for | Standard test runs | docker compose app-under-test, kernel-level needs |
| Nested Docker | Limited | Full daemon available |
The practical takeaway: start on the Docker executor, and switch to machine only when a concrete requirement — usually orchestrating the app under test as sibling containers — forces your hand. Vibium's zero-driver, self-contained-browser design means it never pushes you toward the heavier executor on its own. When you do move to machine, the same install steps and Chrome libraries apply unchanged; only the executor declaration differs.
How do I schedule nightly Vibium regression runs?
Use a scheduled pipeline trigger so a full regression suite runs every night without anyone pushing code. This is the standard pattern for catching flakiness and environment drift that a fast PR pipeline skips. In CircleCI, define the schedule in your project's pipeline settings (or with the modern scheduled-pipelines API) pointing at a dedicated workflow:
workflows:
nightly-regression:
when:
equal: [ "nightly", << pipeline.schedule.name >> ]
jobs:
- test:
# run the full suite, higher parallelism, longer timeoutBecause Vibium is just a pip or npm dependency, a scheduled job needs no special treatment — the same config.yml building blocks apply. Store the JUnit XML with store_test_results so CircleCI shows a pass/fail trend across nights, making a newly flaky test easy to spot. Pair this with failure screenshots and you can triage a broken nightly run in minutes from the artifacts tab.
How does a Vibium CircleCI pipeline compare to Selenium or Playwright?
At a glance, the practical difference in CI is how much browser plumbing each tool forces into your config.yml. Vibium's single-binary, auto-download model removes the two lines that most often break — driver install and version pinning.
| Concern in CircleCI | Vibium | Selenium | Playwright |
|---|---|---|---|
| Browser install | Auto-downloads Chrome for Testing | Bring your own Chrome + chromedriver | npx playwright install step |
| Driver management | None — bundled Go binary | Match chromedriver to Chrome (or Selenium Manager) | None — bundled |
| Base image | cimg/python / cimg/node + libs | Often the pre-baked browsers image | mcr.microsoft.com/playwright or libs |
| Headless switch | Launch flag from env var | Launch option | Launch option / project config |
| AI-native (MCP) | Built-in MCP server | Not built in | Not built in |
| Languages | Python + JavaScript/TypeScript | Java, C#, Python, Ruby, JS | JS/TS, Python, Java, .NET |
When to choose Vibium in CI: you want the leanest possible pipeline with zero driver management, you value that headless is a plain launch flag, and you like having a built-in MCP server so AI agents can drive the same browser your tests use. When Playwright still shines: you need first-class cross-browser coverage across Chromium, Firefox, and WebKit today, or you rely on its mature test runner, fixtures, and reporters. When Selenium fits: you have a large existing Grid, need broad language bindings like Java or C#, or must support legacy browser matrices.
Honest verdict: for a Chrome-first pipeline, Vibium gives you the least CI plumbing of the three and removes driver drift entirely. Playwright remains the stronger pick when true multi-browser breadth or its rich runner ecosystem is the priority. The migration path is gentle — Vibium's find() / click() / type() API maps closely onto both, and your CircleCI job structure barely changes. For the deeper comparison, read Vibium vs Playwright.
What are common CircleCI gotchas with Vibium, and how do I avoid them?
Most Vibium CI failures trace back to a handful of environment issues rather than your test code. Knowing them up front saves a debugging cycle.
- Missing shared libraries. A cryptic Chrome launch failure almost always means a missing
.so. Install the fulllibnss3 … libcairo2list from the configs above; a partial list is the most common cause of "browser won't start." - Not running headless. If a job hangs or Chrome tries to open a window, your launch code is not reading
HEADLESS. Verify the env var is set on the job and that your code checks it, as in the launch snippet above. - Leaking Chrome processes. Always
quit()(Python) orclose()(JS) in a fixture teardown orfinallyblock so a crashed test does not leave zombie Chrome processes that slow the container. - Cold browser download on every run. If setup is slow, your cache key is wrong or the browser path is not in
paths. Confirm~/.cache/vibiumis saved and restored and that the key matches on hit. - Unbalanced parallel containers. One container finishing far later than the rest means you are splitting by name, not timings. Add
--split-by=timingsand make surestore_test_resultsruns so future splits have data. - Flaky assertions on a loaded runner. Rather than adding
sleep(), lean on Vibium's auto-waitingfind()and assert on state, not timing. See waiting strategies and flake-free tests.
Work through this list before blaming your tests — nine times out of ten a red Vibium pipeline is one missing library or a wrong cache key away from green.
Why is Vibium a clean fit for CircleCI?
Three design choices make Vibium pipelines low-maintenance on CircleCI. First, the single Go binary plus auto-downloaded Chrome for Testing means zero driver management and no browser-version drift — the failure that plagues legacy CI. Second, headless is a launch flag driven by one environment variable, not a separate code path, so the suite you debug locally is byte-for-byte the suite that runs in CI. Third, auto-waiting find() keeps tests stable on slower, shared containers without manual sleeps.
Layer CircleCI's own features on top — save_cache for the browser, parallelism with timing-based splits, store_artifacts for failure screenshots, and store_test_results for trends — and you get a pipeline that runs pytest or npm test on every push, stays green, and is fast to debug when it is not. The result is exactly what CI should be: boring, fast, and trustworthy.
Next steps
Frequently asked questions
How do I run Vibium tests in CircleCI?
Add a .circleci/config.yml that uses a Python or Node image, installs Chrome's shared libraries, runs pip install vibium (or npm install vibium), sets HEADLESS=true, and runs your tests. Vibium auto-downloads Chrome for Testing, so there is no driver or browser version to align.
Do I need a Docker image with Chrome for Vibium in CircleCI?
No. Vibium ships as a single Go binary that downloads its own Chrome for Testing on first run. A plain cimg/python or cimg/node image plus Chrome's shared system libraries (libnss3, libgbm1, libasound2) is enough. You do not need the pre-baked browsers image.
How do I speed up Vibium tests in CircleCI?
Cache Vibium's downloaded Chrome directory with save_cache and restore_cache keyed on your Vibium version, then split the suite across parallel containers with parallelism and circleci tests split. Caching removes the browser download and splitting cuts wall-clock time proportionally.
How do I debug a failing Vibium test in CircleCI?
Capture a screenshot on failure, write it to a directory, and upload it with store_artifacts. Because CI runs headless and remote, the artifact screenshot or a Vibium trace is the fastest way to see exactly what the page looked like when the assertion failed.
Can I run Vibium in a CircleCI Orb or scheduled pipeline?
Yes. Vibium is just a pip or npm dependency, so any job that can run Python or Node can run it. Use a scheduled trigger for nightly regression runs, and store JUnit XML with store_test_results so CircleCI shows pass/fail trends over time.
Why is Vibium a good fit for CircleCI compared to Selenium?
Vibium removes driver management entirely: one Go binary downloads a matched Chrome for Testing, so there is never a chromedriver-versus-browser mismatch. Headless is a launch flag, not a separate code path, and auto-waiting find() keeps tests stable on shared runners without manual sleeps.
Vibium is created by Jason Huggins. This is an independent tutorial — see the official Vibium site and GitHub repo for canonical docs.
Related guides
Vibium Best Practices: The Complete Guide
Vibium best practices for reliable browser automation: semantic locators, actionability waits, page objects, isolation, CI, and AI checks.
13 min read→Best PracticesA Complete Vibium CI/CD Pipeline
Build a complete Vibium CI/CD pipeline: install, headless run, parallel shards, artifact capture, and quality gates that block bad merges on every push.
12 min read→Best PracticesData-Driven Testing with Vibium
Data-driven testing with Vibium: feed one browser test many rows from arrays, CSV, or JSON, loop over cases, and keep the automation logic in one place.
15 min read→Best PracticesRun Vibium with Docker Compose
Run Vibium with Docker Compose: orchestrate a headless test service, an app-under-test, mounted artifact volumes, healthchecks, and parallel workers.
15 min read→