Skip to content

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.

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.

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.

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:

Terminal window
Copy-Item .env.example .env

For 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.

Terminal window
docker compose up -d --build
docker compose ps

When the backend shows healthy, open http://localhost:8080.

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
    • …

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:

docker-compose.override.yml
services:
nginx:
ports: !reset ["8081:80"]

Then set APP_BASE_URL=http://localhost:8081 in .env and run docker compose up -d again.

  • The first start is slow. Compose builds the backend, worker, scheduler and frontend images, and each browser service installs @playwright/mcp and its browser every time it starts. Follow progress with docker 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 ps shows the browser service as healthy.
  • 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 a CRITICAL line starting SECURITY: SECRET_KEY is … and reports the problem at GET /api/system/warnings, which only project owners and admins can see. Set a real key before anyone else can reach the app. With PLAINWRIGHT_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).
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.

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:

Terminal window
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build

The worker and scheduler don’t reload. After changing code they use, run docker compose restart worker scheduler.

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.

  1. Start only the infrastructure:

    Terminal window
    docker compose up -d postgres redis playwright-mcp
  2. Create the backend’s virtual environment:

    Terminal window
    cd backend
    python -m venv .venv
    .venv\Scripts\pip install -r requirements.txt
  3. Point the backend at the containers. Settings are read from environment variables or from a .env file in the directory you start the process from, so create backend/.env:

    backend/.env
    SECRET_KEY=the-same-key-as-the-root-env
    DATABASE_URL=postgresql://testai:testai_secret@localhost:5433/testai_db
    REDIS_URL=redis://localhost:6379
    PLAYWRIGHT_MCP_URL=http://localhost:8931/mcp
    SCREENSHOTS_DIR=./screenshots
    # The repository's test-files folder, which the browser container sees as /test-files
    TEST_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
  4. 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 8000
    rq worker --url redis://localhost:6379 test_runs
    python -m app.services.scheduler
  5. Start the frontend dev server from frontend/:

    Terminal window
    npm install
    npm run dev

    Vite serves the app on http://localhost:5173. It proxies /api and /screenshots to http://backend:8000, the backend’s name on the compose network. When the backend runs on your host, change that target in frontend/vite.config.js to http://localhost:8000, or map backend to 127.0.0.1 in 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.