Installation
Plainwright runs as a set of containers started by Docker Compose. This page covers what you need before you start, the settings that matter on day one, what each service does and which ports it uses, and how to run the backend and frontend outside Docker while you develop. For the fastest path, follow the Quickstart instead.
Requirements
Section titled “Requirements”| Requirement | Details |
|---|---|
| Docker | Docker Desktop on Windows (WSL 2 backend) or macOS. On Linux, Docker Engine with the Compose plugin. Compose 2.24 or newer is required, because the compose file marks .env as optional. Check with docker compose version. |
| Git | To clone the repository. |
| CPU and memory | Each browser test runs a real headless browser. A 4 vCPU / 8 GB machine runs 2–3 browser tests in parallel comfortably; give Docker Desktop a similar share. |
| Disk | Several GB for the images (the Playwright browser image is the largest), plus your database and screenshots. |
| Network | The browser services download @playwright/mcp and their browser when they start. Workers need outbound access to the apps you test and to your AI provider. |
| An AI model | An API key for a model that supports tool calling, or a local Ollama. See Choose a model. Site quality tests work without one. |
Choose a model
Section titled “Choose a model”Plainwright needs one model that supports tool or function calling. You can mix the options below; AI providers explains which one a run uses.
Each user adds their own provider in the app (AI providers in the avatar menu, or the first step
of the guided setup): Anthropic, OpenAI, Google Gemini, OpenRouter or Groq. Keys are encrypted with
SECRET_KEY and only shown masked. Nothing to configure on the server.
Set ANTHROPIC_API_KEY in .env. Every project whose owner has no provider of their own then runs
on Anthropic’s claude-haiku-5-5. Useful for a team server where you pay for usage centrally.
ANTHROPIC_API_KEY=sk-ant-...Install Ollama on the host (not in Docker) and pull a model that supports tools:
ollama pull qwen2.5In the app, add an Ollama provider. It needs no key, and the default URL
http://host.docker.internal:11434 is how the containers reach the host. The app lists the models
you have installed and can pull new ones. On Linux, Ollama listens on 127.0.0.1 only by default;
start it with OLLAMA_HOST=0.0.0.0 so the containers can reach it.
Configure the environment
Section titled “Configure the environment”All settings live in one .env file in the repository root. Compose reads it for variable
substitution and passes it to the backend, worker and scheduler. Start from the example:
Copy-Item .env.example .envcp .env.example .envFor a local install you only need to look at a few of them:
| Variable | Default | Set it when |
|---|---|---|
SECRET_KEY |
public development key | Always, before you store anything. It signs logins and encrypts provider keys, vault secrets and saved logins. Use a random value of at least 32 characters (openssl rand -hex 32 prints 64) and the same value on every service. |
PLAINWRIGHT_ENV |
development |
On any install other people use: set it to production (or prod). The backend then refuses to start while SECRET_KEY is empty, a known default or shorter than 32 characters, instead of only warning. It also hides /metrics unless METRICS_TOKEN is set. |
ANTHROPIC_API_KEY |
empty | You want a server-wide fallback model (see above). |
APP_BASE_URL |
http://localhost:8080 |
You reach the app on another address. It is used for links in emails and alerts and for sign-in callbacks. |
CORS_ORIGINS |
localhost on ports 8080, 3000 and 5173 |
You serve the frontend from another origin. Comma-separated, such as https://plainwright.example.com. |
WORKER_REPLICAS |
2 |
You want more (or fewer) tests to run in parallel. Each worker runs one test at a time. |
SMTP_HOST … SMTP_FROM |
empty | You want invitation, password-reset and alert emails. |
Every other setting (storage, sign-in with Google or GitHub, monitoring, billing, cost caps) is documented in .env.example and in Configuration.
Start the stack
Section titled “Start the stack”docker compose up -d --builddocker compose psWhen the backend shows healthy, open http://localhost:8080.
Services and ports
Section titled “Services and ports”| Service | Host port | What it does |
|---|---|---|
nginx |
8080 (all interfaces) | The public entry point. Proxies /api and /screenshots to the backend and everything else to the frontend. This is the URL you open. |
frontend |
3000 (localhost only) | The React app, built and served by its own nginx. You normally go through port 8080. |
backend |
8000 (localhost only) | FastAPI REST API and authentication. Runs database migrations on start; exposes /health and, internally, /metrics. |
worker |
none | RQ workers that execute runs, one test at a time each (WORKER_REPLICAS, default 2). |
scheduler |
none | Starts cron-scheduled tests and marks runs whose worker died as errors. Run exactly one. |
playwright-mcp |
8931 (localhost only) | Headless Chromium driven over Playwright MCP. |
playwright-mcp-firefox, playwright-mcp-webkit |
none | The same for Firefox and WebKit. |
postgres |
5433 (localhost only) | All data. Containers reach it as postgres:5432; the host port can be changed with POSTGRES_HOST_PORT. |
redis |
6379 (localhost only) | The test_runs job queue and live run events. |
Data lives in two named volumes, postgres_data and screenshots. Files you upload for tests live
in the test-files/ folder of the repository, which is shared with the browser services.
- docker-compose.yml the stack
- docker-compose.dev.yml optional live reload for the backend
- .env your settings (create it from .env.example)
Directorybackend/ API, worker, scheduler and migrations
- …
Directoryfrontend/ React app
- …
- nginx/nginx.conf the reverse proxy on port 8080
Directorytest-files/ uploaded test assets, one folder per project
- …
Change a port
Section titled “Change a port”Put overrides in a docker-compose.override.yml next to docker-compose.yml; Compose merges it
automatically. For example, to serve the app on port 8081:
services: nginx: ports: !reset ["8081:80"]Then set APP_BASE_URL=http://localhost:8081 in .env and run docker compose up -d again.
First-run notes
Section titled “First-run notes”- The first start is slow. Compose builds the backend, worker, scheduler and frontend images,
and each browser service installs
@playwright/mcpand its browser every time it starts. Follow progress withdocker compose logs -f playwright-mcp. - Wait for the browsers before the first browser test. Workers start as soon as the browser
containers do, not when they are ready. A run started too early ends in error (“Could not reach
or drive the browser automation server”). Re-run it once
docker compose psshows the browser service ashealthy. - Migrations run automatically. The backend upgrades the database on every start. Several backends can start at once safely. Workers and the scheduler don’t migrate.
- The development key warning. With an empty, default or short
SECRET_KEY, the backend still starts in development mode, but logs aCRITICALline startingSECURITY: SECRET_KEY is …and reports the problem atGET /api/system/warnings, which only project owners and admins can see. Set a real key before anyone else can reach the app. WithPLAINWRIGHT_ENV=production, the backend refuses to start instead. - Your first account is not special. Anyone who can reach the app can register, and each user owns the projects they create. Share projects through Settings → Members (Team and roles).
Stop, update and reset
Section titled “Stop, update and reset”| Task | Command |
|---|---|
| Stop everything, keep data | docker compose down |
| Update to the latest code | git pull, then docker compose up -d --build |
| Follow a service’s logs | docker compose logs -f worker |
| Run more workers for a while | docker compose up -d --scale worker=4 |
| Delete all data and start over | docker compose down -v |
Rebuild the backend, worker and scheduler together after pulling changes. They share the backend code, and a worker running old code against a new database is a common source of confusing errors.
Develop locally
Section titled “Develop locally”Live reload in Docker
Section titled “Live reload in Docker”The default stack runs the API without auto-reload. While you work on backend code, add the
development override. It mounts backend/app into the container and restarts the API on every
change:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --buildThe worker and scheduler don’t reload. After changing code they use, run
docker compose restart worker scheduler.
Run the backend and frontend on the host
Section titled “Run the backend and frontend on the host”Keep Postgres, Redis and the browser in Docker, and run the Python and Node processes yourself. You need Python 3.12 and Node.js 20 or newer.
-
Start only the infrastructure:
Terminal window docker compose up -d postgres redis playwright-mcp -
Create the backend’s virtual environment:
Terminal window cd backendpython -m venv .venv.venv\Scripts\pip install -r requirements.txtTerminal window cd backendpython -m venv .venv.venv/bin/pip install -r requirements.txt -
Point the backend at the containers. Settings are read from environment variables or from a
.envfile in the directory you start the process from, so createbackend/.env:backend/.env SECRET_KEY=the-same-key-as-the-root-envDATABASE_URL=postgresql://testai:testai_secret@localhost:5433/testai_dbREDIS_URL=redis://localhost:6379PLAYWRIGHT_MCP_URL=http://localhost:8931/mcpSCREENSHOTS_DIR=./screenshots# The repository's test-files folder, which the browser container sees as /test-filesTEST_FILES_DIR=../test-files# Only for local development: lets a host-run backend reach services on localhost,# such as an Ollama at http://localhost:11434 (refused by the outbound-request guard otherwise)# ALLOW_INTERNAL_TARGETS=true -
Start the API, a worker and (optionally) the scheduler, each in its own terminal from
backend/with the virtual environment active:Terminal window uvicorn app.main:app --reload --port 8000rq worker --url redis://localhost:6379 test_runspython -m app.services.scheduler -
Start the frontend dev server from
frontend/:Terminal window npm installnpm run devVite serves the app on
http://localhost:5173. It proxies/apiand/screenshotstohttp://backend:8000, the backend’s name on the compose network. When the backend runs on your host, change that target infrontend/vite.config.jstohttp://localhost:8000, or mapbackendto127.0.0.1in your hosts file.
To run the unit tests (pytest for the backend, vitest for the frontend), see
Contributing. They use SQLite and a fake model, so they need no Docker,
network or API key.