Skip to content

Latest commit

 

History

History
436 lines (351 loc) · 12.3 KB

File metadata and controls

436 lines (351 loc) · 12.3 KB

Docker and Containers

When to use: Running Playwright tests in containers for reproducible environments, CI pipelines, or local development with consistent browser versions. Essential when your team needs identical test environments across machines.

Quick Reference

# Official Playwright Docker images
docker pull mcr.microsoft.com/playwright:v1.52.0-noble     # Ubuntu 24.04, all browsers
docker pull mcr.microsoft.com/playwright:v1.52.0-jammy      # Ubuntu 22.04, all browsers

# Run tests in container
docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/playwright:v1.52.0-noble \
  npx playwright test

# Check your Playwright version (must match image tag)
npx playwright --version

Patterns

Pattern 1: Running Tests in the Official Image

Use when: Quick, reproducible test runs without building a custom image. Avoid when: You need application services (database, API) running alongside -- use docker-compose instead.

# Run all tests
docker run --rm \
  -v $(pwd):/app \
  -w /app \
  mcr.microsoft.com/playwright:v1.52.0-noble \
  bash -c "npm ci && npx playwright test"

# Run with environment variables
docker run --rm \
  -v $(pwd):/app \
  -w /app \
  -e CI=true \
  -e BASE_URL=http://host.docker.internal:3000 \
  mcr.microsoft.com/playwright:v1.52.0-noble \
  bash -c "npm ci && npx playwright test"

# Run and extract report
docker run --rm \
  -v $(pwd):/app \
  -w /app \
  -v $(pwd)/playwright-report:/app/playwright-report \
  mcr.microsoft.com/playwright:v1.52.0-noble \
  bash -c "npm ci && npx playwright test"

Pattern 2: Custom Dockerfile

Use when: You need additional system dependencies, pre-installed npm packages, or a smaller image with only certain browsers. Avoid when: The official image works as-is.

# Dockerfile.playwright
FROM mcr.microsoft.com/playwright:v1.52.0-noble

WORKDIR /app

# Copy package files first for better layer caching
COPY package.json package-lock.json ./
RUN npm ci

# Copy the rest of the project
COPY . .

# Default command
CMD ["npx", "playwright", "test"]
# Build and run
docker build -f Dockerfile.playwright -t my-e2e-tests .
docker run --rm my-e2e-tests

# Run with specific options
docker run --rm my-e2e-tests npx playwright test --project=chromium

# Extract report
docker run --rm -v $(pwd)/reports:/app/playwright-report my-e2e-tests

Slim image with only Chromium:

# Dockerfile.playwright-chromium
FROM node:20-slim

# Install only Chromium dependencies
RUN npx playwright install --with-deps chromium

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

CMD ["npx", "playwright", "test", "--project=chromium"]

Pattern 3: Docker Compose with Application Stack

Use when: Tests need the full application stack: web server, database, cache, and Playwright running together. Avoid when: Tests run against a remote environment (staging/prod) -- no local services needed.

# docker-compose.yml
services:
  # Application under test
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=test
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/test
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started

  # Database
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: test
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    tmpfs:
      - /var/lib/postgresql/data  # RAM disk for speed

  # Cache
  cache:
    image: redis:7-alpine

  # Playwright test runner
  e2e:
    image: mcr.microsoft.com/playwright:v1.52.0-noble
    working_dir: /app
    volumes:
      - .:/app
      - /app/node_modules  # prevent host node_modules from overriding
    environment:
      - CI=true
      - BASE_URL=http://app:3000
    depends_on:
      - app
    command: bash -c "npm ci && npx playwright test"
    profiles:
      - test  # only start with: docker compose --profile test up
# Run the full stack with tests
docker compose --profile test up --abort-on-container-exit --exit-code-from e2e

# Run just the app (for local dev)
docker compose up app

# Run tests against already-running stack
docker compose --profile test run --rm e2e npx playwright test

# Tear down everything
docker compose --profile test down -v

Pattern 4: Extracting Reports and Traces from Containers

Use when: You need test artifacts (HTML reports, traces, screenshots) accessible on the host after container tests complete. Avoid when: CI handles artifact collection natively (GitHub Actions, GitLab artifacts).

# Method 1: Bind mount the output directories
docker run --rm \
  -v $(pwd):/app \
  -v $(pwd)/playwright-report:/app/playwright-report \
  -v $(pwd)/test-results:/app/test-results \
  -w /app \
  mcr.microsoft.com/playwright:v1.52.0-noble \
  bash -c "npm ci && npx playwright test"

# Method 2: Copy artifacts from a stopped container
docker run --name e2e-run \
  -v $(pwd):/app \
  -w /app \
  mcr.microsoft.com/playwright:v1.52.0-noble \
  bash -c "npm ci && npx playwright test" || true

docker cp e2e-run:/app/playwright-report ./playwright-report
docker cp e2e-run:/app/test-results ./test-results
docker rm e2e-run

# View the report
npx playwright show-report ./playwright-report

Docker Compose for report extraction:

# docker-compose.yml (add to e2e service)
services:
  e2e:
    image: mcr.microsoft.com/playwright:v1.52.0-noble
    working_dir: /app
    volumes:
      - .:/app
      - ./playwright-report:/app/playwright-report
      - ./test-results:/app/test-results
    # ... rest of config

Pattern 5: CI Container Strategies

Use when: Your CI environment benefits from containerized test execution.

GitHub Actions -- container job:

# .github/workflows/playwright.yml
jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright:v1.52.0-noble
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx playwright test
        env:
          HOME: /root

GitLab CI -- image directive:

# .gitlab-ci.yml
test:
  image: mcr.microsoft.com/playwright:v1.52.0-noble
  script:
    - npm ci
    - npx playwright test

Jenkins -- Docker agent:

// Jenkinsfile
pipeline {
    agent {
        docker {
            image 'mcr.microsoft.com/playwright:v1.52.0-noble'
            args '-u root'
        }
    }
    stages {
        stage('Test') {
            steps {
                sh 'npm ci'
                sh 'npx playwright test'
            }
        }
    }
}

Pattern 6: Development Container (devcontainer)

Use when: Your team uses VS Code Dev Containers or GitHub Codespaces and needs Playwright available in the development environment. Avoid when: Everyone installs Playwright locally and version differences don't cause issues.

// .devcontainer/devcontainer.json
{
  "name": "Playwright Dev",
  "image": "mcr.microsoft.com/playwright:v1.52.0-noble",
  "features": {
    "ghcr.io/devcontainers/features/node:1": {
      "version": "20"
    }
  },
  "postCreateCommand": "npm ci",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-playwright.playwright"
      ]
    }
  },
  "forwardPorts": [3000, 9323],
  "remoteUser": "root"
}

Decision Guide

Scenario Approach Why
Simple CI pipeline Official Playwright image as CI image Browsers pre-installed; zero browser install time
Tests need database + cache Docker Compose with app, db, cache, e2e services Full stack in isolated containers
Team needs identical environments Dev Container or custom Dockerfile Eliminate "works on my machine"
Only testing Chromium Slim image: node:20-slim + install --with-deps chromium Smaller image, faster pulls
Cross-browser testing Official Playwright image (has all browsers) All three engines pre-installed
Local development Run directly on host, not in container Faster iteration, easier debugging
CI with artifact extraction Bind mount report/results dirs or use CI artifact upload Reports accessible after container exits
Image Size Browsers Base OS
mcr.microsoft.com/playwright:v1.52.0-noble ~2 GB Chromium, Firefox, WebKit Ubuntu 24.04
mcr.microsoft.com/playwright:v1.52.0-jammy ~2 GB Chromium, Firefox, WebKit Ubuntu 22.04
Custom slim (Chromium only) ~800 MB Chromium Depends on base

Anti-Patterns

Anti-Pattern Problem Do This Instead
Image tag doesn't match @playwright/test version Browser binaries incompatible with Playwright library Always match: v1.52.0 image for @playwright/test@1.52.0
Using latest tag Unpredictable; image updates can break tests Pin to exact version: v1.52.0-noble
Installing browsers inside container at runtime Wastes 60-90 seconds on every run Use official image (browsers pre-installed) or build custom image with browsers baked in
Running as non-root without configuring sandbox Chromium sandbox fails with permission errors Run as root (-u root) or disable sandbox (--no-sandbox in launch args)
Bind-mounting node_modules from host Platform-specific binaries (macOS vs Linux) cause crashes Use anonymous volume: -v /app/node_modules
No health checks on dependent services Tests start before database is ready Add healthcheck to db service; use depends_on: condition: service_healthy
Building application inside the Playwright container Large image, slow builds, wrong base for your app Separate app and e2e containers in docker-compose

Troubleshooting

"browserType.launch: Executable doesn't exist" in container

Cause: Playwright version in package.json doesn't match the Docker image version.

Fix: Ensure exact version match:

# Check your version
npm ls @playwright/test
# @playwright/test@1.52.0

# Use matching image
docker pull mcr.microsoft.com/playwright:v1.52.0-noble

Tests fail with "net::ERR_CONNECTION_REFUSED" in docker-compose

Cause: Tests are trying to reach localhost:3000 but the app is in a different container.

Fix: Use the service name as hostname and configure baseURL:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
  },
  // Disable webServer in Docker -- app is managed by docker-compose
  ...(process.env.CI ? {} : {
    webServer: {
      command: 'npm run dev',
      url: 'http://localhost:3000',
      reuseExistingServer: true,
    },
  }),
});
# docker-compose.yml
e2e:
  environment:
    - BASE_URL=http://app:3000  # "app" is the service name

Permission denied on mounted volumes

Cause: Container runs as root but host files are owned by your user, or vice versa.

Fix: Match user IDs or run as root:

# Run as your host user
docker run --rm -u $(id -u):$(id -g) \
  -v $(pwd):/app -w /app \
  mcr.microsoft.com/playwright:v1.52.0-noble \
  npx playwright test

# Or run as root (simpler, fine for CI)
docker run --rm \
  -v $(pwd):/app -w /app \
  mcr.microsoft.com/playwright:v1.52.0-noble \
  npx playwright test

Container tests are much slower than local

Cause: Docker Desktop on macOS/Windows has I/O overhead for bind-mounted volumes.

Fix: Copy files into the container instead of mounting:

# Dockerfile.playwright
FROM mcr.microsoft.com/playwright:v1.52.0-noble
WORKDIR /app
COPY . .
RUN npm ci
CMD ["npx", "playwright", "test"]

Or use delegated mount on macOS:

docker run --rm \
  -v $(pwd):/app:delegated \
  -w /app \
  mcr.microsoft.com/playwright:v1.52.0-noble \
  bash -c "npm ci && npx playwright test"

Related