Plainwright is configured entirely with environment variables. This page lists all of them:
the application settings in backend/app/core/config.py, the few
variables read straight from the environment, and the ones only deployment tooling uses.
The frontend has no settings of its own: it calls the API at the relative path /api.
Use this page to look up a setting. To set up a feature end to end, follow its guide, for
example Sign-in and email or
Object storage.
Three services read them: the backend (API), the worker (runs tests) and the
scheduler (cron runs and the stuck-run reaper). The Read by column says which services
need each variable. With Docker Compose all three get the same environment, so there’s
nothing extra to do. On Render, Railway or Fly.io, give every app service the same values.
Names aren’t case-sensitive, and unknown names are ignored. A misspelled variable doesn’t
stop anything from starting; the setting just keeps its default.
Booleans accept true/false, 1/0, yes/no and on/off. An empty value for
a number or boolean doesn’t parse and the service fails to start, so delete the line
instead of leaving it empty.
Values are read once, at start. After editing .env, recreate the containers with
docker compose up -d. docker compose restart keeps the old environment.
Compose uses .env twice: for ${…} substitution in docker-compose.yml, and as an optional
env_file that hands every variable in it to the backend, worker and scheduler. A few values
are fixed by the compose file and win over .env, so the in-network addresses stay correct:
The compose file also passes PLAINWRIGHT_ENV (fallback development), SECRET_KEY,
APP_BASE_URL, ANTHROPIC_API_KEY and the storage, observability and billing settings with
explicit fallbacks. An empty or missing SECRET_KEY
falls back to the insecure built-in default, supersecretkey123, so a first local start works.
With PLAINWRIGHT_ENV=production the backend refuses to start on that fallback; see
Production mode.
These are the fields of the Settings class in backend/app/core/config.py, with the code’s
defaults. .env.example uses the same values with two exceptions: it leaves SECRET_KEY empty
for you to fill in, and its DATABASE_URL points at localhost:5433, where Compose publishes
Postgres, for running the backend directly on the host.
production (or prod) makes the backend refuse to start while SECRET_KEY is missing, a known default or shorter than 32 characters, and hides /metrics unless METRICS_TOKEN is set. Any other value is development mode: the backend starts, logs the problem at CRITICAL, and shows it to project owners and admins as a banner. Set production on every install other people use.
SECRET_KEY
supersecretkey123
backend · worker · scheduler
Signs sign-in tokens and OAuth state, and derives the key that encrypts provider API keys, vault secrets, saved logins and integration credentials. Set a random value of at least 32 characters and use the same one on every service. Don’t edit it in place: changing it by hand signs everyone out and makes stored secrets unreadable. Change it with the rotation command, which re-encrypts them.
ACCESS_TOKEN_EXPIRE_MINUTES
10080
backend
How long a sign-in token stays valid (10080 = 7 days). Tokens can’t be revoked server-side, so a shorter lifetime limits a leaked token.
ALGORITHM
HS256
backend
JWT signing algorithm for sign-in tokens and OAuth state. Leave it as HS256.
Comma-separated browser origins allowed to call the API cross-origin. The app is same-origin behind nginx, so this only matters for a separately served frontend (e.g. the Vite dev server). * allows any origin; credentials are never allowed.
APP_BASE_URL
http://localhost:8080
backend · worker
Public URL people open, e.g. https://plainwright.example.com. Used for links in invitation, password-reset and alert emails, run links in Slack/Discord/webhook alerts, Stripe return URLs, and the OAuth callback when OAUTH_REDIRECT_BASE is empty.
Folder for screenshots, visual diffs and evidence when STORAGE_BACKEND=local. The backend serves it at /screenshots/.
TEST_FILES_DIR
/test-files
backend · worker
Per-project uploaded test files, stored as <dir>/<project_id>/<file>. The backend writes uploads (25 MB per file); workers list the files into test prompts. The browser must see the same folder at /test-files.
PLAYWRIGHT_MCP_URL
http://playwright-mcp:8931/mcp
backend · worker
Chromium Playwright MCP server. Workers use it for browser runs; the backend uses it for the AI Crawler. Only the host and port matter: the client opens /sse on them.
PLAYWRIGHT_MCP_URL_FIREFOX
http://playwright-mcp-firefox:8931/mcp
worker
Firefox MCP server, used for runs with browser firefox.
PLAYWRIGHT_MCP_URL_WEBKIT
http://playwright-mcp-webkit:8931/mcp
worker
WebKit MCP server, used for runs with browser webkit.
Server-wide fallback model (Anthropic claude-haiku-5-5), used when neither the project nor its owner has an AI provider. Leave empty to require every user to add a provider.
MAX_TRIALS
10
backend · worker · scheduler
Most repeats a run may make for a pass-rate test. Never more than 10, whatever this says.
MAX_EVAL_ROWS_PER_RUN
500
worker
Most dataset rows scored by one eval or RAG run. Each row costs a target call plus a judge call.
MAX_CONVERSATION_TURNS
12
worker
Most turns a simulated user may take in a conversation or red-team test.
JUDGE_MAX_TOKENS
800
worker
Minimum output-token budget for a judge reply. The budget grows with the number of criteria or metrics (about 120 tokens each, up to 4,000).
A2A_TIMEOUT_S
60
—
Reserved. Not read by the current code: A2A tests use the test’s own timeout_s (default 60 s, 1 to 300 s).
MCP_TARGET_TIMEOUT_S
60
worker
Per-request timeout in seconds for an MCP server under test, when the test doesn’t set its own.
Lets server-side requests to user-supplied URLs reach cloud-metadata addresses and the internal hosts postgres, redis, backend, worker and scheduler. Leave false unless you trust every user on a single-user install. See Security model.
Google OAuth client id. The Google button appears when this and the secret are set.
GOOGLE_CLIENT_SECRET
(empty)
backend
Google OAuth client secret.
GITHUB_CLIENT_ID
(empty)
backend
GitHub OAuth App client id. The GitHub button appears when this and the secret are set.
GITHUB_CLIENT_SECRET
(empty)
backend
GitHub OAuth App client secret.
OAUTH_REDIRECT_BASE
(empty)
backend
Public origin for OAuth callbacks (<base>/api/oauth/<provider>/callback). Empty uses APP_BASE_URL. When the resulting base starts with https://, the sign-in cookie is also marked Secure.
local (the SCREENSHOTS_DIR folder) or s3 for any S3-compatible store (r2 and minio are aliases). s3 without S3_BUCKET falls back to local with a warning.
S3_ENDPOINT_URL
(empty)
backend · worker
S3 API endpoint for R2, MinIO and other non-AWS stores. Leave empty for AWS S3. When set, path-style addressing is used.
S3_BUCKET
(empty)
backend · worker
Bucket name. Objects are written under screenshots/.
S3_REGION
auto
backend · worker
Region: auto for R2, the bucket’s region for AWS (e.g. eu-west-1), us-east-1 for MinIO.
S3_ACCESS_KEY_ID
(empty)
backend · worker
Access key with read and write access to the bucket. Empty uses boto3’s default credential chain (e.g. an IAM role).
S3_SECRET_ACCESS_KEY
(empty)
backend · worker
Secret for S3_ACCESS_KEY_ID.
S3_PUBLIC_BASE_URL
(empty)
backend · worker
Public bucket or CDN URL. When set, artifacts link straight to <url>/screenshots/<key>; when empty, /api/files/<key> redirects to a presigned URL valid for one hour.
Sentry DSN. When set, each service reports errors tagged with its service name. No PII or request bodies are sent.
LOG_FORMAT
text
backend · worker · scheduler
text (readable) or json (one JSON object per line on stdout, uvicorn and RQ logs included).
METRICS_ENABLED
true
backend
Serve Prometheus metrics at /metrics on the backend port. false makes it answer 404.
METRICS_TOKEN
(empty)
backend
When set, /metrics requires Authorization: Bearer <token>. When empty, production mode answers /metrics with 404, and development mode answers only clients on loopback or a private network (the compose network, the host). Set it whenever a scraper reaches the backend from elsewhere. See Monitoring.
These are read with os.environ rather than the settings class. Set them the same way: in
.env (Compose passes them through) or in your platform’s environment.
Variable
Default
Read by
What it does
SSRF_DNS_CHECK
true
backend · worker
Also resolve host names in user-supplied URLs, and refuse them when any address is a cloud-metadata or link-local one. Lookups time out after 2 s and are cached for 60 s. Set false only where DNS lookups aren’t possible.
SENTRY_ENVIRONMENT
production
backend · worker · scheduler
Sentry environment tag.
SENTRY_TRACES_SAMPLE_RATE
0
backend · worker · scheduler
Share of requests sent to Sentry as performance traces, from 0 to 1. 0 sends errors only.
Port Render routes traffic to. The blueprint sets 8000.
The Postgres container’s own credentials are written in docker-compose.yml
(POSTGRES_USER=testai, POSTGRES_PASSWORD=testai_secret, POSTGRES_DB=testai_db). The
database is reachable only on the compose network and the host’s loopback address. The internal
names stay testai so existing volumes keep working. Postgres applies POSTGRES_PASSWORD only
when it first creates the volume, so to change the password of an existing database, run
ALTER USER testai PASSWORD '…' and update both the compose file and DATABASE_URL.
PLAINWRIGHT_ENV tells the backend whether other people depend on this install. It checks
SECRET_KEY once at start, before touching the database:
SECRET_KEY is…
PLAINWRIGHT_ENV=development (default)
PLAINWRIGHT_ENV=production
A random value of 32+ characters
Starts normally.
Starts normally.
Empty, or a publicly known default such as supersecretkey123
Starts, logs a CRITICAL line starting SECURITY:, and shows owners and admins a banner.
Refuses to start: “Refusing to start with PLAINWRIGHT_ENV=production: SECRET_KEY is …”
Shorter than 32 characters
Same as above.
Refuses to start.
The banner comes from GET /api/system/warnings, which answers project owners and admins with
{"warnings": [{"code", "severity", "message", "docs"}]} (codes default_secret_key and
weak_secret_key) and everyone else with an empty list. Production mode also hides /metrics
unless METRICS_TOKEN is set.
The Render blueprint sets PLAINWRIGHT_ENV=production. Elsewhere,
set it yourself once SECRET_KEY is in place.