Skip to main content
Running Stably-powered Playwright tests inside Docker gives you a clean, repeatable environment that behaves the same on your laptop, in CI, or on self-hosted runners. The workflow mirrors the local test flow and the GitHub Actions setup—use the same npx stably test command, but package it alongside the services your application needs (front end, APIs, databases, queues, and more).

Prerequisites

  • Docker (or another OCI-compatible runtime) installed
  • Node.js 18+ available in your base image
  • A Playwright project initialized in your repository
  • Stably installed as a project dependency (npm install --save-dev stably)

Quick Setup

1. Extend your existing application image with Playwright dependencies

Add the Playwright browsers and keep your build steps for the app under test. The example below installs dependencies, builds a front end, and leaves the app ready to run inside the container:
Dockerfile
FROM node:18-bullseye-slim AS tester

WORKDIR /app

# Install dependencies first for better layer caching
COPY package*.json ./
RUN npm ci

# Copy source code (front end, tests, utilities, etc.)
COPY . .

# Build or seed any assets your app needs before tests execute
RUN npm run build

# Ensure Playwright browsers are available inside the container
RUN npx playwright install --with-deps

# Default command mirrors running tests locally
CMD ["npx", "stably", "test"]
If you already build separate images for your front end, back end, or supporting services, keep those Dockerfiles untouched and introduce a dedicated test image (like the tester stage above) that shares the same source tree.

2. Build the image or test stage

docker build -t stably-tests .

3. Run tests alongside your stack

Start the supporting containers (API, database, etc.), then run the test image with the same environment you would use locally:
docker run --rm \
  --network my-app-network \
  -e STABLY_API_KEY=$STABLY_API_KEY \
  -e BASE_URL=http://web:3000 \
  stably-tests
Using a shared Docker network allows the test container to reach the services it is validating. The example sets BASE_URL to the DNS name of a front-end container (web), but you can add any env vars, secrets, or config files your tests rely on.
Store your Stably API key securely—use environment files, Docker secrets, or your orchestrator’s secret manager instead of hardcoding it in the image.

Coordinate with Other Services

  • Compose or orchestration tools: Launch your services with Docker Compose, Kubernetes, or your preferred orchestrator. Attach the Stably test container to the same network so it can call real APIs and front ends.
  • Wait for readiness: Use health checks, dockerize, wait-for-it, or your own scripts to ensure dependent services are ready before tests run.
  • Seed data: Run migrations or seed scripts in the same compose file so the environment mirrors production as closely as necessary.
docker-compose.yml
services:
  web:
    build: ./frontend
    environment:
      API_URL: http://api:4000
  api:
    build: ./api
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 10
  tests:
    build:
      context: .
      target: tester
    depends_on:
      - web
      - api
    environment:
      STABLY_API_KEY: ${STABLY_API_KEY}
      BASE_URL: http://web:3000
    command: ["npx", "stably", "test"]
Run everything together and exit with the test result:
docker compose up --build --exit-code-from tests

Customize Playwright and Stably Flags

Pass any additional CLI flags exactly as you would locally. For example:
docker run --rm \
  -e STABLY_API_KEY=$STABLY_API_KEY \
  stably-tests \
  --project=chromium \
  --grep @critical
Everything supported by npx playwright test works with npx stably test, including reporters, --headed, and configuration overrides.

Parallel Runs with Multiple Containers

Shard your suite by spinning up multiple instances of the same test image. Each container can target the same backing services or its own isolated stack, depending on how you model the compose file or orchestrator:
docker run --rm \
  --network my-app-network \
  -e STABLY_API_KEY=$STABLY_API_KEY \
  stably-tests --shard=1/4 &

docker run --rm \
  --network my-app-network \
  -e STABLY_API_KEY=$STABLY_API_KEY \
  stably-tests --shard=2/4 &

docker run --rm \
  --network my-app-network \
  -e STABLY_API_KEY=$STABLY_API_KEY \
  stably-tests --shard=3/4 &

docker run --rm \
  --network my-app-network \
  -e STABLY_API_KEY=$STABLY_API_KEY \
  stably-tests --shard=4/4 &

wait
In Compose, add separate services that share the same build target but override the command:
docker-compose.yml
services:
  tests-shard-1:
    build:
      context: .
      target: tester
    environment:
      STABLY_API_KEY: ${STABLY_API_KEY}
    command: ["npx", "stably", "test", "--shard=1/4"]
  tests-shard-2:
    build:
      context: .
      target: tester
    environment:
      STABLY_API_KEY: ${STABLY_API_KEY}
    command: ["npx", "stably", "test", "--shard=2/4"]
# ...repeat for additional shards
Stably aggregates results from every shard automatically in the dashboard.

Persist Playwright Artifacts

Mount a volume if you want to inspect local reports in addition to the Stably dashboard:
docker run --rm \
  -e STABLY_API_KEY=$STABLY_API_KEY \
  -v "$(pwd)/playwright-report:/app/playwright-report" \
  stably-tests \
  --reporter=html
The reports folder stays in sync with your host so you can open it after the container exits.

Next Steps

I