Environment Variables
Local development only. Each service reads its configuration from environment variables. Defaults are designed to work out-of-the-box for local dev.
Local Startup — .env.local
Copy from .env.local.example.
| Variable | Default | Description |
|---|---|---|
WEB_PORT | 3100 | Port used by both the setup app and the normal web app |
DOCS_PORT | 3104 | Port used by the local Docusaurus docs site started by ./start-local.sh |
SETUP_INSTALLER_PORT | 3103 | Port used by the local setup installer service |
POSTGRES_PORT | 5431 | Host port mapped to the Docker-backed Postgres container |
POSTGRES_HOST | 127.0.0.1 | Hostname used when deriving the default local Prisma connection |
POSTGRES_DB | playrunner | Database name for the standard local Postgres container |
POSTGRES_USER | postgres | Username for the standard local Postgres container |
POSTGRES_PASSWORD | postgres | Password for the standard local Postgres container |
PUBSUB_EMULATOR_PORT | 8084 | Host port mapped to fixed container port 8085 for the Docker-backed Pub/Sub emulator |
PUBSUB_EMULATOR_HOST | 127.0.0.1:8084 | Host-facing Pub/Sub emulator endpoint used by the API |
LOCAL_PUBSUB_PROJECT_ID | playrunner-local | Project ID used for local Pub/Sub emulator topics and subscriptions |
DATABASE_URL | (optional) | Explicit Prisma datasource URL override for both setup defaults and the normal API startup path |
VITE_DEFAULT_DATABASE_URL | (optional) | Override only the database URL prefilled in the setup form |
VITE_DOCS_URL | Derived from DOCS_PORT by default | Optional full override for the header Docs link during repo-root local startup |
./start-local.sh loads this file first, uses it to start Docker-backed services, and passes the derived defaults into the setup app. If a legacy repo-root .env still exists and .env.local does not, the script renames it to .env.local automatically.
API Server — apps/api/.env
Created from apps/api/.env.example during setup when needed, then updated by the setup installer. During normal local startup, ./start-local.sh also keeps DATABASE_URL aligned with the repo-root .env.local when you are still using the standard Docker-backed Postgres settings.
The GCP variables in this file are only needed when this API instance should trigger GCP-backed workflow executions. Standard local Docker workflows can leave them unset.
| Variable | Default | Description |
|---|---|---|
PORT | 3011 | Port the Express API server listens on |
DATABASE_URL | Derived from repo-root .env.local by default | Prisma datasource used for app data and workflow execution events. In the standard local flow this resolves to postgresql://postgres:postgres@127.0.0.1:<POSTGRES_PORT>/playrunner?schema=public unless you explicitly override DATABASE_URL in the repo-root .env.local. |
GCP_PROJECT | local-dev | GCP project ID used for Cloud Storage / Cloud Run integrations |
ORCHESTRATOR_PORT | 3012 | Host port the Orchestrator Docker container is mapped to |
ORCHESTRATOR_URL | http://localhost:3012 | Full URL used by the API to communicate with the Orchestrator |
LOCAL_ORCHESTRATOR_IMAGE | playrunner-orchestrator | Docker image name for the local Orchestrator container built by start-local.sh. Remote GCP runs use the image URI template saved in the GCP integration settings instead. |
EDITOR_API_URL_DOCKER | http://host.docker.internal:3011 | API server URL as seen from inside Docker containers; local Playwright runners still use it to upload compressed output archives |
PUBSUB_EMULATOR_HOST | Inherited from repo-root .env.local | Host-facing Pub/Sub emulator endpoint used by the API for local workflow event ingest |
PUBSUB_EMULATOR_HOST_DOCKER | host.docker.internal:<PUBSUB_EMULATOR_PORT> | Container-facing Pub/Sub emulator endpoint injected into local Orchestrator and Playwright runner containers |
LOCAL_PUBSUB_PROJECT_ID | playrunner-local | Project ID used by the API when creating local emulator topics and subscriptions |
PLAYRUNNER_CREDENTIAL_ENCRYPTION_KEYS | Generated locally | JSON object mapping positive integer versions to base64-encoded 32-byte keys. Used to encrypt integration credentials and Authentication Profile browser state at rest. |
PLAYRUNNER_CREDENTIAL_ENCRYPTION_KEY_VERSION | 1 for a new local setup | Active key version used for new credential writes. The matching key must exist in PLAYRUNNER_CREDENTIAL_ENCRYPTION_KEYS. |
PLAYRUNNER_LOCAL_AUTHENTICATION_ENABLED | Enabled outside Cloud Run | Set to true or false to explicitly enable or disable local interactive Authentication Profile capture. |
PLAYRUNNER_AUTHENTICATION_BROWSER_EXECUTABLE_PATH | (optional) | Absolute path to the Chrome or Chromium executable used for interactive Authentication Profile capture. |
PLAYRUNNER_AUTHENTICATION_BROWSER_CHANNEL | chrome | Playwright browser channel used when testing a stored Authentication Profile and no executable path is configured. If the default channel is unavailable, Playrunner falls back to its Chromium executable. |
GCP_PUBSUB_WORKFLOW_EVENTS_TOPIC | playrunner-workflow-events | Pub/Sub topic name used for workflow execution events. Local runs create/use this topic in the emulator; GCP runs use the managed topic created by OAuth provisioning or optional Terraform. |
GCS_BUCKET_PREFIX | (required for GCP) | Required only for GCP-backed workflow execution. Prefix used when the API creates per-workflow GCS output buckets before triggering Cloud Run. |
GCS_PROJECT_ID | (optional fallback for GCP) | Optional fallback project for GCS clients when a selected project is not supplied by the GCP workflow request. |
Remote GCP runner settings such as Cloud Run region, Orchestrator service name,
Orchestrator min/max instances, CPU idle policy, and generated image URI
templates are stored in the GCP connection config, not in
apps/api/.env.
./start-local.sh creates a random credential-encryption key in
apps/api/.env before starting the API when no keyring exists, and retains it
across restarts. Do not commit this file, restrict it to the operating-system
user, and do not replace the key while saved connections or Authentication
Profiles still depend on it. Without the matching version, the API cannot
decrypt those records. Back up the local keyring separately from PostgreSQL;
someone who obtains both can decrypt the saved credentials and browser state.
In the standard Docker setup, encrypted Authentication Profile records persist
in the playrunner-postgres-data volume mounted at
/var/lib/postgresql/data. If DATABASE_URL points to another PostgreSQL
server, the records are stored there instead.
Web App — apps/frontend/.env
Optional. The standard local flow does not require this file.
| Variable | Default | Description |
|---|---|---|
VITE_API_URL | http://127.0.0.1:3011 | Target for the Vite dev-server proxy (/api/* and /outputs/*) when you run the frontend outside ./start-local.sh |
VITE_SETUP_INSTALLER_URL | http://127.0.0.1:3103 | Target for the Vite dev-server proxy (/setup-api/*) when you run the setup app outside ./start-local.sh |
VITE_DOCS_URL | https://docs.playrunner.dev | Target for the header Docs link when you run the frontend outside ./start-local.sh |
GEMINI_API_KEY | (empty) | API key for Gemini AI features; injected at runtime in production |
APP_URL | (empty) | Self-referential URL; not needed for local dev |
When you use ./start-local.sh, it exports the correct local proxy targets automatically, so this file is only needed for standalone frontend debugging.
Orchestrator — apps/runners/orchestrator/.env
This file is not used when the Orchestrator runs as a Docker container (all variables are injected via docker run -e flags by the API). It is provided as a reference for running the Orchestrator directly on the host during debugging.
| Variable | Default | Description |
|---|---|---|
PORT | 3012 | Port the Orchestrator Express server listens on when you run it directly against the standard local API settings |
EDITOR_API_URL | http://localhost:3011 | API server URL used by local runners for output archive uploads |
ORCHESTRATOR_EXECUTOR_TIMEOUT_MS | 300000 | Positive-integer timeout in milliseconds for one package-owned node executor. The default is five minutes. The current API-managed local container uses the default unless you inject an override into a custom launch. |
PUBSUB_EMULATOR_HOST | (optional) | Container-facing Pub/Sub emulator endpoint when running the Orchestrator locally against the local event bus |
GCP_PUBSUB_WORKFLOW_EVENTS_TOPIC | playrunner-workflow-events | Pub/Sub topic name used for workflow execution events |
GCP_PROJECT | local-dev | GCP project ID used for Cloud Storage / Cloud Run integrations |
PLAYWRIGHT_IMAGE_BASE | playrunner-playwright-runner | Base Docker image prefix for Playwright runner containers; the orchestrator appends -typescript or -python |
GCP_CLOUD_RUN_API_BASE_URL | (required for GCP) | Cloud Run API base URL used to create/run Playwright jobs |
GCP_CLOUD_RUN_LOCATION | (required for GCP) | Cloud Run region for GCP Playwright jobs |
GCP_PLAYWRIGHT_IMAGE_URI_TEMPLATE | (required for GCP) | URI template for already-pushed Playwright runner images that Cloud Run Jobs can pull. Supports {projectId}, {runtime}, and {version}. |
GCP_PLAYWRIGHT_JOB_NAME_TEMPLATE | playrunner-{runtime} | Optional Cloud Run job name template; supports {runtime}, {version}, {cpu}, {memory}, and {nodeId}. If {nodeId} is omitted, Playrunner appends a stable node suffix so sibling Playwright nodes can run in parallel instead of sharing one Cloud Run Job. |
Playwright Runner
The Playwright runner does not use a .env file. Its entire configuration is passed as a single JSON-encoded string in the PAYLOAD environment variable, injected by the Orchestrator when it runs docker run.
PAYLOAD Structure
{
"data": {
"repository": "org/repo-name",
"branch": "main",
"folder": "e2e/",
"action": "clone",
"executionAuthToken": "signed-per-execution-secret",
"testScript": null,
"nodeId": "node-abc123",
"testId": "550e8400-e29b-41d4-a716-446655440000",
"editorApiUrl": "http://host.docker.internal:3011",
"eventTransport": {
"type": "gcp_pubsub",
"projectId": "playrunner-local",
"topicName": "playrunner-workflow-events"
},
"runnerControl": {
"controlSubscriptionName": "projects/playrunner-local/subscriptions/playrunner-runner-control-...",
"statusSubscriptionName": "projects/playrunner-local/subscriptions/playrunner-runner-status-...",
"topicName": "playrunner-workflow-events",
"type": "gcp_pubsub"
}
},
"github": {
"accessToken": "gha_...",
"refreshToken": "ghr_...",
"clientId": "...",
"clientSecret": "..."
}
}
eventTransport and runnerControl use the same payload shape for local Docker
and GCP. Local Docker varies only by environment: PUBSUB_EMULATOR_HOST points
the Pub/Sub client at the emulator. GCP omits that emulator variable and uses the
managed Pub/Sub API. The control/status subscriptions are polled with
non-blocking Pub/Sub pulls; the polling interval is owned by Playrunner rather
than by Pub/Sub long-poll timing.
The API constructs the github runner settings from the encrypted server-side
connection immediately before execution. This payload is sent only to the
relevant runner; it is not the public integration response used by the browser.
Repository and branch dropdowns call the GitHub package API and receive only
non-secret metadata.
The runner also receives these additional environment variables:
| Variable | Description |
|---|---|
GCP_PROJECT | GCP project ID (local-dev) |
PUBSUB_EMULATOR_HOST | Pub/Sub emulator endpoint for local Docker workflow runs |
| Supported user-defined env vars | Injected from the Environment node's configured variables. See Environment variable names for naming rules and reserved names. |
How Variables Flow Through the Stack
.env.local
└─ PUBSUB_EMULATOR_HOST → API local Pub/Sub ingest endpoint
└─ LOCAL_PUBSUB_PROJECT_ID → local emulator project ID
.env (API)
└─ DATABASE_URL → Prisma + workflow event storage
└─ EDITOR_API_URL_DOCKER → passed to local runners for output uploads
└─ PUBSUB_EMULATOR_HOST_DOCKER → passed to local runner containers (-e)
Orchestrator container (env from API's docker run)
└─ GCP_PROJECT → passed to Playwright container (-e)
└─ PUBSUB_EMULATOR_HOST → passed to local Playwright container (-e)
└─ PAYLOAD (full config) → passed to Playwright container (-e)