Q32 of 42 · Playwright

How do you handle environment-specific config and secret management in Playwright?

PlaywrightSeniorplaywrightconfigurationsecretsenvironmentssenior

Short answer

Short answer: Single `playwright.config.ts` driven by env vars (`process.env.BASE_URL`, etc.). Per-environment values come from CI variables, not committed files. Use projects for env-specific test selection. Secrets stay in CI vault; local development uses `.env` files (gitignored) loaded via `dotenv`.

Detail

The clean pattern: one config file, env vars drive the per-environment differences.

// playwright.config.ts
import 'dotenv/config';                    // load .env locally
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
    extraHTTPHeaders: {
      'X-API-Key': process.env.API_KEY ?? '',
    },
  },
  projects: [
    { name: 'chromium', use: { /* ... */ } },
  ],
});

Where values come from:

  • Local dev: a .env file (gitignored) loaded by dotenv at the top of the config.
  • CI: CI provider's secret store. GitHub Actions secrets.PROD_API_KEY, CircleCI context, Buildkite cluster secrets.
  • Per-env CI workflows: separate workflows for staging vs prod, each with its own secret bindings.

Per-environment test selection via projects:

projects: [
  { name: 'staging-smoke', use: { baseURL: process.env.STAGING_URL }, testMatch: /\.smoke\./ },
  { name: 'prod-readonly', use: { baseURL: process.env.PROD_URL }, testMatch: /\.readonly\./ },
]

For multi-role auth secrets: store credentials per role in CI vars (CYPRESS_ADMIN_PASSWORD, CYPRESS_VIEWER_PASSWORD). The setup project uses them; tests never see them directly.

Hard rules:

  • Never commit .env files. Add to .gitignore.
  • Never log secrets. console.log(process.env) in CI accidentally leaks them. Test code should never print env vars.
  • Don't hardcode baseURL per environment in different config files. One config + env vars is cleaner.
  • Production E2E is its own conversation. Most teams don't run write-side tests against prod; if you do, dedicated test tenant with explicit data isolation is required.

For local dev convenience, ship a .env.example (committed) with placeholder names so newcomers know what to set:

# .env.example
BASE_URL=http://localhost:3000
ADMIN_EMAIL=admin@x.com
ADMIN_PASSWORD=

The senior signal: knowing the boundary (CI vars for secrets, .env for local convenience, never committed), per-env test selection via projects, and the production-testing caveat.

// WHAT INTERVIEWERS LOOK FOR

Single config + env vars (not multiple config files), CI secrets vs local `.env`, project-based env selection, and production caveat.

// COMMON PITFALL

Committing a `.env` file with 'staging credentials only' — staging credentials are still credentials, and they leak into git history forever.