.agents/skills/playwright-best-practices/infrastructure-ci-cd/docker.md
When to use: Running tests in containers for reproducible environments, CI pipelines, or consistent browser versions across team machines.
Run tests without building a custom image:
docker run --rm \
-v $(pwd):/app \
-w /app \
-e CI=true \
-e BASE_URL=http://host.docker.internal:3000 \
mcr.microsoft.com/playwright:v1.48.0-noble \
bash -c "npm ci && npx playwright test"
Extract reports with bind mounts:
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.48.0-noble \
bash -c "npm ci && npx playwright test"
Build a custom image when you need additional dependencies or pre-installed packages:
FROM mcr.microsoft.com/playwright:v1.48.0-noble
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["npx", "playwright", "test"]
Chromium-only slim image:
FROM node:latest-slim
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"]
Full application stack with database, cache, and test runner:
services:
app:
build: .
ports:
- '3000:3000'
environment:
- NODE_ENV=test
- DATABASE_URL=postgresql://postgres:postgres@db:5432/test
depends_on:
db:
condition: service_healthy
db:
image: postgres:latest-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
e2e:
image: mcr.microsoft.com/playwright:v1.48.0-noble
working_dir: /app
volumes:
- .:/app
- /app/node_modules
environment:
- CI=true
- BASE_URL=http://app:3000
depends_on:
- app
command: bash -c "npm ci && npx playwright test"
profiles:
- test
Run commands:
docker compose --profile test up --abort-on-container-exit --exit-code-from e2e
docker compose --profile test down -v
GitHub Actions:
jobs:
test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.48.0-noble
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test
env:
HOME: /root
GitLab CI:
test:
image: mcr.microsoft.com/playwright:v1.48.0-noble
script:
- npm ci
- npx playwright test
Jenkins:
pipeline {
agent {
docker {
image 'mcr.microsoft.com/playwright:v1.48.0-noble'
args '-u root'
}
}
stages {
stage('Test') {
steps {
sh 'npm ci'
sh 'npx playwright test'
}
}
}
}
VS Code Dev Container or GitHub Codespaces configuration:
{
"name": "Playwright Dev",
"image": "mcr.microsoft.com/playwright:v1.48.0-noble",
"features": {
"ghcr.io/devcontainers/features/node:latest": {
"version": "20"
}
},
"postCreateCommand": "npm ci",
"customizations": {
"vscode": {
"extensions": ["ms-playwright.playwright"]
}
},
"forwardPorts": [3000, 9323],
"remoteUser": "root"
}
| Scenario | Approach |
|---|---|
| Simple CI pipeline | Official image as CI container |
| Tests need database + cache | Docker Compose with app, db, e2e services |
| Team needs identical environments | Dev Container or custom Dockerfile |
| Only testing Chromium | Slim image with install --with-deps chromium |
| Cross-browser testing | Official image (all browsers pre-installed) |
| Local development | Run directly on host for faster iteration |
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Installing browsers at runtime | Wastes 60-90 seconds per run | Use official image or bake browsers into custom image |
| Running as non-root without sandbox config | Chromium sandbox permission errors | Run as root or disable sandbox |
Bind-mounting node_modules from host | Platform-specific binary crashes | Use anonymous volume: -v /app/node_modules |
| No health checks on dependent services | Tests start before database ready | Add healthcheck with depends_on: condition: service_healthy |
| Building application inside Playwright container | Large image, slow builds | Separate app and e2e containers |
Playwright version mismatch with Docker image. Ensure @playwright/test version matches image tag:
npm ls @playwright/test
docker pull mcr.microsoft.com/playwright:v<matching-version>-noble
Tests trying to reach localhost instead of service name. Configure baseURL:
import {defineConfig} from '@playwright/test'
export default defineConfig({
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
},
})
e2e:
environment:
- BASE_URL=http://app:3000
Match user IDs or run as root:
docker run --rm -u $(id -u):$(id -g) \
-v $(pwd):/app -w /app \
mcr.microsoft.com/playwright:v1.48.0-noble \
npx playwright test
Docker Desktop I/O overhead. Copy files instead of mounting:
FROM mcr.microsoft.com/playwright:v1.48.0-noble
WORKDIR /app
COPY . .
RUN npm ci
CMD ["npx", "playwright", "test"]
Or use delegated mount:
docker run --rm \
-v $(pwd):/app:delegated \
-w /app \
mcr.microsoft.com/playwright:v1.48.0-noble \
bash -c "npm ci && npx playwright test"