Resource library

QA How-To

How to Use Playwright webServer config (2026)

Learn playwright webServer config for local and CI test servers, readiness URLs, baseURL, multiple services, logging, timeouts, and clean shutdowns in CI.

20 min read | 3,495 words

TL;DR

Playwright webServer config starts a command once for the test run, waits until its URL or output readiness condition succeeds, runs the tests, and stops the process it launched. Pair webServer.url with use.baseURL, set reuseExistingServer to !process.env.CI, and use multiple entries when the application requires more than one local service.

Key Takeaways

  • Configure webServer at the top level of playwright.config.ts with a command and readiness URL.
  • Use the same origin for webServer.url and use.baseURL so startup checks and test navigation agree.
  • Reuse an existing server locally, but require a clean runner-owned server in CI.
  • Point readiness at a lightweight endpoint that proves dependencies are usable, not merely that a port opened.
  • Use an array when frontend and backend processes must start for the same test run.
  • Set cwd, env, logging, timeout, and graceful shutdown deliberately for reproducible lifecycle behavior.

The playwright webServer config starts a local application before Playwright tests, waits for a readiness signal, and stops the process after the run. A reliable configuration uses a deterministic command, an application-level readiness URL, a matching baseURL, and different reuse behavior for local development and CI.

This guide covers the full lifecycle: basic setup, readiness semantics, timeouts, monorepos, environment variables, multiple services, HTTPS, logging, shutdown, and CI design. All configuration examples use current Playwright Test options.

TL;DR

// playwright.config.ts
import { defineConfig } from '@playwright/test';

const appURL = 'http://127.0.0.1:4173';

export default defineConfig({
  testDir: './tests',
  use: {
    baseURL: appURL,
    trace: 'on-first-retry',
  },
  webServer: {
    command: 'npm run build && npm run preview -- --host 127.0.0.1 --port 4173',
    url: appURL + '/health',
    reuseExistingServer: !process.env.CI,
    timeout: 120_000,
    stdout: 'pipe',
    stderr: 'pipe',
  },
});
Option Purpose Practical default
command Starts the app or service A deterministic test or preview script
url Proves the server is accepting requests Lightweight health or app route
reuseExistingServer Reuses a process already on the URL !process.env.CI
timeout Limits startup wait Based on measured cold start
cwd Selects command working directory App package in a monorepo
env Adds process environment values Test-safe runtime settings
stdout, stderr Controls server logs Pipe in CI or diagnosis
gracefulShutdown Requests clean termination SIGTERM with bounded timeout on supported systems

1. What playwright webServer config Controls

webServer is a top-level Playwright Test configuration option. Before tests begin, the runner checks the configured readiness target. If the target is unavailable, it launches command and waits. After the test run, Playwright terminates the process it started.

The server is not launched once per test or once per worker. It belongs to the overall test-run lifecycle. That keeps startup cost out of individual tests and prevents every parallel worker from competing for the same port.

A minimal setup is:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  webServer: {
    command: 'npm run dev',
    url: 'http://127.0.0.1:3000',
    reuseExistingServer: !process.env.CI,
  },
  use: {
    baseURL: 'http://127.0.0.1:3000',
  },
});

The webServer URL is the address Playwright probes for readiness. The baseURL is a browser-context option used to resolve relative navigation and supported URL matchers. They often share an origin, but they perform different jobs.

Playwright adds PLAYWRIGHT_TEST=1 to the launched process environment. Applications can use that flag for safe test-only behavior, such as choosing an isolated database, but never expose insecure backdoors or production credentials because a test flag exists.

If a remote BASE_URL is already supplied, conditionally omit the local server:

const externalURL = process.env.BASE_URL;

export default defineConfig({
  use: {
    baseURL: externalURL ?? 'http://127.0.0.1:3000',
  },
  webServer: externalURL
    ? undefined
    : {
        command: 'npm run dev -- --host 127.0.0.1 --port 3000',
        url: 'http://127.0.0.1:3000',
        reuseExistingServer: !process.env.CI,
      },
});

This supports local test startup and remote environment execution without maintaining two config files.

2. Create a Basic TypeScript Configuration

Start with a package script that has one documented purpose:

{
  "scripts": {
    "dev:e2e": "vite --host 127.0.0.1 --port 4173",
    "test:e2e": "playwright test"
  }
}

Then connect it to Playwright:

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

const appURL = 'http://127.0.0.1:4173';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  reporter: [['list'], ['html', { open: 'never' }]],

  use: {
    baseURL: appURL,
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },

  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
  ],

  webServer: {
    command: 'npm run dev:e2e',
    url: appURL,
    reuseExistingServer: !process.env.CI,
    timeout: 120_000,
  },
});

A test can now navigate with a relative URL:

import { test, expect } from '@playwright/test';

test('opens the sign-in page', async ({ page }) => {
  await page.goto('/login');

  await expect(page.getByRole('heading', { name: 'Sign in' }))
    .toBeVisible();
});

Use 127.0.0.1 consistently when the server binds there. Mixing localhost, IPv4, and IPv6 can cause a readiness check to target a different listener than the browser.

The web server does not install dependencies or database schema unless command does so. Keep preparation explicit in CI or define a test startup script that exits on failure before serving. A command that continues after a failed migration can produce confusing browser failures.

For broader runner options, see Playwright configuration best practices.

3. Understand URL Readiness and Health Endpoints

Playwright polls webServer.url until the server returns an accepted HTTP status. Current behavior treats 2xx, 3xx, 400, 401, 402, or 403 as ready. That allows a protected root route to serve as a signal, but a dedicated health endpoint is usually clearer.

A useful health endpoint proves the application can perform required work:

webServer: {
  command: 'npm run start:test',
  url: 'http://127.0.0.1:3000/health/ready',
  timeout: 120_000,
}

Avoid a health route that returns 200 before database migrations, route compilation, or dependent services are ready. A port can accept TCP connections while the first real test still receives 503.

The best readiness depth depends on the suite:

Readiness check What it proves Limitation
Root page HTTP app responds May redirect or hide dependency failure
Static asset Web server serves files Does not prove application dependencies
Liveness endpoint Process is running Often too shallow
Readiness endpoint Required dependencies work Must remain fast and deterministic
Authenticated page Protected stack responds Needs credentials and can be brittle

Keep the endpoint lightweight because Playwright may poll it during startup. It should not mutate state or create audit noise.

The deprecated port option waits for a listener, but new configurations should use url. URL readiness provides an HTTP-level signal and supports path-specific checks.

If you configure both url and the output-based wait option, current Playwright treats the server as started when at least one condition succeeds. It is an OR relationship, not an AND gate. Use one primary readiness model unless that fallback behavior is intentional.

4. Pair webServer.url With use.baseURL

These settings are related but not interchangeable:

const origin = 'http://127.0.0.1:3000';

export default defineConfig({
  webServer: {
    command: 'npm run start:test',
    url: origin + '/health/ready',
  },
  use: {
    baseURL: origin,
  },
});

webServer.url can include /health/ready, while baseURL normally uses the application origin. page.goto('/orders') then opens http://127.0.0.1:3000/orders.

baseURL is also considered by page.route, page.waitForRequest, page.waitForResponse, and page.waitForURL when supported path forms are used. This makes test URLs portable:

const responsePromise = page.waitForResponse('/api/orders');

await page.goto('/orders');
const response = await responsePromise;

Keep protocol, host, and port aligned. If the server readiness URL uses http://127.0.0.1:3000 but baseURL uses http://localhost:3000, cookies, origin rules, redirects, and service workers may behave differently.

Do not include a page-specific path such as /login in baseURL merely because it is the readiness target. baseURL should be the base against which all test paths resolve.

An environment-driven setup should validate its URL early:

const baseURL = process.env.BASE_URL ?? 'http://127.0.0.1:3000';
const parsed = new URL(baseURL);

if (!['http:', 'https:'].includes(parsed.protocol)) {
  throw new Error('BASE_URL must use http or https');
}

Failing configuration with a clear error is better than allowing every navigation test to fail.

5. Configure playwright webServer config for Local and CI Runs

Local developers often already run the app. CI should normally own a clean server.

webServer: {
  command: 'npm run start:test',
  url: 'http://127.0.0.1:3000/health/ready',
  reuseExistingServer: !process.env.CI,
}

With reuseExistingServer true, Playwright uses an existing process when the URL is available. If none is available, it runs command. With false, an existing process on the target causes an error rather than silently testing an unknown build.

That distinction protects CI from stale or contaminated servers. A runner should test the checked-out commit, not whatever process happens to hold the port.

Local reuse has a tradeoff. A developer may unknowingly test a different branch or environment. Make the running app display build identity, expose it in a health response, or provide a clean command when correctness matters more than startup convenience.

Choose the server mode deliberately:

  • Development server for fast local iteration and source maps.
  • Production build plus preview server for release-like asset behavior.
  • Application start command for server-rendered frameworks.
  • Remote base URL with webServer omitted for deployed environment checks.

A Vite release-like command can be:

webServer: {
  command:
    'npm run build && npm run preview -- --host 127.0.0.1 --port 4173',
  url: 'http://127.0.0.1:4173/health',
  reuseExistingServer: false,
  timeout: 180_000,
}

Do not add retries to startup commands that hide deterministic build failures. Let the command exit nonzero and preserve its logs.

6. Set Startup Timeout, Output, and Useful Diagnostics

webServer.timeout limits how long Playwright waits for readiness. The default is 60 seconds. Measure cold starts in CI before choosing a larger value.

webServer: {
  command: 'npm run start:test',
  url: 'http://127.0.0.1:3000/health/ready',
  timeout: 180_000,
  stdout: 'pipe',
  stderr: 'pipe',
}

stdout defaults to ignore. stderr defaults to pipe. Piping both is valuable in CI when startup failures need application logs, although noisy dev-server output can obscure the Playwright report.

Use a named server for clearer prefixes, especially with several processes:

webServer: {
  name: 'Customer Portal',
  command: 'npm run start:test',
  url: 'http://127.0.0.1:3000/health/ready',
  stdout: 'pipe',
  stderr: 'pipe',
}

For Playwright's own lifecycle diagnostics, run with the web-server debug namespace:

DEBUG=pw:webserver npx playwright test

When startup times out, inspect these facts:

  1. Did command remain running or exit immediately?
  2. Does it bind the same host and port as url?
  3. Can the readiness path return an accepted status?
  4. Are required environment variables available?
  5. Is a migration, compilation, or dependency still pending?
  6. Did another process take the port?
  7. Does HTTPS use an untrusted local certificate?
  8. Is the configured cwd correct?

Do not automatically double the timeout. First distinguish a slow correct startup from a command that can never satisfy readiness.

7. Use cwd and env in Monorepos and Test Modes

By default, the command runs with the configuration file's directory as its working directory. In a monorepo, set cwd to the package containing the application:

import path from 'node:path';
import { defineConfig } from '@playwright/test';

export default defineConfig({
  webServer: {
    command: 'npm run start:e2e',
    cwd: path.join(__dirname, 'apps/customer-portal'),
    url: 'http://127.0.0.1:3100/health/ready',
    env: {
      APP_ENV: 'e2e',
      DATABASE_NAME: 'customer_portal_e2e',
      LOG_LEVEL: 'warn',
    },
  },
  use: {
    baseURL: 'http://127.0.0.1:3100',
  },
});

Environment settings should be non-secret configuration. Pass credentials through the CI secret store into process.env, not as literals committed to playwright.config.ts.

Playwright supplies the launched process with inherited environment values and PLAYWRIGHT_TEST=1. The env option adds or overrides values for this server. Use names that application code already understands rather than adding test-only forks throughout the product.

A safer pattern validates required secrets:

const databaseURL = process.env.E2E_DATABASE_URL;

if (!databaseURL) {
  throw new Error('E2E_DATABASE_URL is required');
}

export default defineConfig({
  webServer: {
    command: 'npm run start:e2e',
    url: 'http://127.0.0.1:3100/health/ready',
    env: {
      E2E_DATABASE_URL: databaseURL,
    },
  },
});

Do not point destructive E2E cleanup at production. Use a restricted test database or tenant and fail configuration when the environment does not have an approved test marker.

8. Start Multiple Frontend and Backend Services

Pass an array when the suite needs more than one local process:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  webServer: [
    {
      name: 'API',
      command: 'npm run start:test',
      cwd: './apps/api',
      url: 'http://127.0.0.1:3333/health/ready',
      timeout: 120_000,
      reuseExistingServer: !process.env.CI,
      stdout: 'pipe',
      stderr: 'pipe',
    },
    {
      name: 'Web',
      command: 'npm run dev -- --host 127.0.0.1 --port 4173',
      cwd: './apps/web',
      url: 'http://127.0.0.1:4173',
      timeout: 120_000,
      reuseExistingServer: !process.env.CI,
      stdout: 'pipe',
      stderr: 'pipe',
    },
  ],
  use: {
    baseURL: 'http://127.0.0.1:4173',
  },
});

Playwright launches multiple configured servers for the run and waits for readiness. Each entry should have a unique name and target.

If the frontend requires the API URL, make it part of the frontend server environment:

{
  name: 'Web',
  command: 'npm run dev -- --host 127.0.0.1 --port 4173',
  cwd: './apps/web',
  url: 'http://127.0.0.1:4173',
  env: {
    API_BASE_URL: 'http://127.0.0.1:3333',
  },
}

Readiness order is not a substitute for application resilience. Because processes may start concurrently, the frontend command should not crash permanently if the API takes a few seconds. Its readiness endpoint, however, should reflect whatever dependencies the browser suite truly needs.

Do not use webServer to start every infrastructure component by hand when Docker Compose or a dedicated environment script already manages lifecycle reliably. One webServer command can run that orchestrator, provided it remains attached, exposes a readiness target, and responds to shutdown.

See Playwright testing in Docker for container-specific browser and service considerations.

9. Use Output-Based Readiness for Non-HTTP Processes

Current Playwright web server configuration can wait for matching stdout or stderr output. This helps with a background compiler, proxy, emulator, or process that does not expose a useful HTTP endpoint.

webServer: {
  name: 'Background Worker',
  command: 'npm run worker:test',
  wait: {
    stdout: /Worker ready to process jobs/,
  },
  timeout: 60_000,
  stdout: 'pipe',
  stderr: 'pipe',
}

Both stdout and stderr regular expressions can be supplied. Named capture groups from a matched expression are stored in process.env for later use:

wait: {
  stdout: /Worker listening on (?<worker_port>\d+)/,
}

Use a stable, machine-readable readiness line. Do not match a generic word such as started that could appear in a warning or dependency log.

If both url and wait are present, readiness succeeds when at least one condition is met. That behavior can hide a broken health endpoint if a log line matches first. Use both only when either signal is genuinely sufficient.

Output readiness is weaker than an application-level health check when the process can print ready before dependencies work. Prefer url for HTTP applications. Reserve wait for processes whose usable state is accurately represented by output.

Log formats are product contracts once automation depends on them. If the worker changes its message, tests will stop at startup. Keep the line deliberate and cover it in service startup tests when it is important.

10. Handle HTTPS and Graceful Shutdown

A local HTTPS server may use a certificate that the readiness probe does not trust:

webServer: {
  command: 'npm run start:https',
  url: 'https://127.0.0.1:3443/health/ready',
  ignoreHTTPSErrors: true,
},
use: {
  baseURL: 'https://127.0.0.1:3443',
  ignoreHTTPSErrors: true,
}

The webServer setting affects the readiness request. The use setting affects browser contexts. Configure both only for controlled test certificates. Production certificate validation should not be bypassed in release monitoring.

By default, Playwright forcefully terminates the process group it launched. A service that needs cleanup can request a graceful signal:

webServer: {
  command: 'npm run start:test',
  url: 'http://127.0.0.1:3000/health/ready',
  gracefulShutdown: {
    signal: 'SIGTERM',
    timeout: 5_000,
  },
}

Playwright sends the selected signal and force-kills the process group if it does not exit within the timeout. A timeout of zero means no follow-up SIGKILL. SIGTERM and SIGINT graceful behavior is not supported on Windows, so design cross-platform scripts accordingly.

The server must handle the signal, stop accepting new work, release database connections, and exit. A parent script that spawns a detached child can defeat lifecycle management and leave the port occupied for the next run.

Test shutdown locally by running a focused suite twice. If the second run reports a port collision, fix process ownership rather than adding port-kill commands that may terminate unrelated work.

11. Integrate the Server Lifecycle Into CI

A CI job should install locked dependencies, install matching browser binaries, run Playwright, and preserve reports. webServer handles the application process inside the test command.

name: e2e
on:
  pull_request:

jobs:
  playwright:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm run test:e2e
        env:
          CI: "true"

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 7

The configuration should set reuseExistingServer to false in CI through !process.env.CI. It should also pipe enough server output to diagnose a failed start.

Separate infrastructure setup from browser execution when it has an independent lifecycle. For example, start a disposable database service through the CI platform, migrate it in a dedicated step, then let webServer own only the application. This makes a migration failure distinct from a browser failure.

Avoid shell commands that background the app in one step and run tests in another unless the CI platform explicitly manages that process. Playwright webServer already provides readiness and cleanup, which reduces leaked processes.

For pipeline matrix and artifacts, see Playwright CI best practices.

12. Debug playwright webServer config Failures Systematically

Treat startup as a small system with command, process, endpoint, and cleanup boundaries.

Run the command directly from the configured cwd:

cd apps/web
npm run start:e2e

From another terminal, probe the exact readiness URL:

curl -i http://127.0.0.1:4173/health/ready

Then run one test with lifecycle logging:

DEBUG=pw:webserver npx playwright test tests/smoke.spec.ts --project=chromium

Interpret common symptoms:

Symptom Likely cause First check
Command exits immediately Script, build, env, or port failure Piped stderr and exit code
Startup timeout Wrong URL, slow dependency, shallow bind Exact curl target
Existing server error in CI Stale process or port collision Runner process ownership
Tests get connection refused Host mismatch or server exited Bound address and server logs
First test gets 503 Readiness endpoint is too shallow Dependency-aware health
Second run gets port in use Child process survived shutdown Signal handling and process tree
Local passes, CI fails cwd, env, resources, or build differences Reproduce CI command locally

Do not solve startup errors with page waits. Browser tests begin only after webServer readiness, so page.waitForTimeout cannot repair a server that never became usable.

Interview Questions and Answers

Q: What is Playwright webServer used for?

It manages a local process around the Playwright test run. The runner starts the configured command when needed, waits for URL or output readiness, runs tests, and stops the process it owns.

Q: What is the difference between webServer.url and use.baseURL?

webServer.url is polled to decide when startup is ready. use.baseURL resolves relative browser navigation and supported route or network matchers. They usually share an origin, while the readiness URL may include a health path.

Q: Why set reuseExistingServer to !process.env.CI?

Local developers can reuse an app they already started, which speeds iteration. CI should fail if an unknown process owns the target so the job tests the checked-out build in a clean lifecycle.

Q: How do you start two services?

Provide an array of webServer entries with separate commands, URLs, names, timeouts, and working directories. Set the browser baseURL to the frontend and pass the backend address through application configuration.

Q: What should a readiness endpoint verify?

It should respond quickly and prove the dependencies required by the browser suite are usable. A port-only or liveness response may be too shallow if the first real route still waits for migrations or a database.

Q: How do you debug a webServer timeout?

Run the exact command from cwd, inspect piped output, curl the exact URL, and enable pw:webserver debug logging. Confirm host, port, protocol, environment, dependency readiness, and whether the command exited.

Q: When is output-based wait appropriate?

Use it for a non-HTTP worker, compiler, emulator, or proxy whose stable readiness is expressed by a deliberate log line. For an HTTP app, a dependency-aware URL usually proves more.

Q: How does graceful shutdown work?

Configure SIGTERM or SIGINT with a timeout. Playwright requests shutdown and force-kills after the timeout if necessary. The application must handle the signal and release resources, and Windows does not support those signals through this option.

Common Mistakes

  • Placing webServer inside use or a project instead of at the top configuration level.
  • Using different hosts or protocols for readiness and browser baseURL.
  • Pointing readiness at a port or route that becomes available before dependencies.
  • Setting reuseExistingServer true in CI and testing a stale process.
  • Using the deprecated port option in new configuration instead of url.
  • Increasing timeout without checking whether command exited or the URL is wrong.
  • Ignoring server stdout and stderr when CI startup fails.
  • Running a monorepo command from the wrong cwd.
  • Hardcoding credentials or production database URLs in the config.
  • Assuming multiple webServer entries start in a safe dependency order.
  • Configuring both url and wait while expecting both to succeed.
  • Bypassing HTTPS validation outside a controlled local certificate.
  • Spawning detached child processes that Playwright cannot stop.
  • Trying to fix server startup with sleeps inside browser tests.

Conclusion

A strong playwright webServer config makes the test environment part of the automated contract. Give Playwright one deterministic command, a meaningful readiness signal, a consistent baseURL, visible diagnostics, and a process it can stop cleanly.

Begin with the minimal object, then add cwd, env, multiple services, output readiness, HTTPS, or graceful shutdown only when the architecture requires them. Validate the lifecycle by running the suite twice locally and once from a clean CI job. A predictable server boundary makes every browser failure easier to trust.

Interview Questions and Answers

Describe the Playwright webServer lifecycle.

Before tests, Playwright checks readiness and starts command when required. It waits for the configured URL or output condition, runs the suite once ready, and stops the process it launched after the run. The lifecycle is per test run, not per test.

Why prefer a URL over the deprecated port option?

A URL verifies an HTTP response and can target a dependency-aware readiness path. An open port only proves that something accepted a connection. URL readiness provides stronger evidence that the application can serve tests.

How do webServer.url and baseURL work together?

The webServer URL gates startup, while baseURL resolves relative navigation and supported URL matchers in browser contexts. I keep their protocol, host, and port consistent. The readiness URL can add a health path.

What does reuseExistingServer change?

When true, Playwright reuses a process already reachable at the target, otherwise it starts command. When false, an existing listener is an error. I use local reuse for convenience and disable it in CI for build integrity.

How would you configure a monorepo app?

I set cwd to the application package, use its documented start script, and point url at that service's readiness endpoint. Backend and frontend can be separate array entries. Shared addresses are passed through explicit environment configuration.

How do you choose a startup timeout?

I measure clean CI cold starts and allow bounded headroom. If startup times out, I first inspect command exit, logs, host, port, health behavior, and dependencies. A larger timeout is appropriate only when the server is genuinely progressing.

How would you debug a server that is ready locally but not in CI?

I compare cwd, scripts, environment variables, Node and dependency versions, host binding, port ownership, build mode, and dependent services. I pipe output, curl the exact URL in the CI environment, and enable pw:webserver diagnostics.

How do multiple web servers affect readiness?

Each configured process has its own command and readiness condition. Playwright can launch the entries for one run and waits for them to become usable. Applications should tolerate concurrent startup rather than depend on array order.

What risk comes from both url and wait options?

Readiness succeeds when either condition is met. A ready log line can therefore mask a failing health endpoint. I normally use one signal, or document why either signal is sufficient.

When should gracefulShutdown be configured?

Use it when the server must flush work or release resources before exit. I select SIGTERM or SIGINT with a bounded timeout and verify the application handles it. I also account for the option's signal limitations on Windows.

How should secrets reach the web server?

CI injects secrets into the process environment from its secret store. The config validates required values and forwards only what the test service needs. I never commit credentials or use production data stores for destructive E2E tests.

Should webServer start databases and all infrastructure?

Only if one reliable orchestration command already owns that lifecycle. Often CI services or Docker Compose should manage infrastructure, while webServer owns the application. Clear boundaries make startup failures and cleanup easier to diagnose.

Frequently Asked Questions

How do I start a server before Playwright tests?

Add a top-level webServer object to playwright.config.ts with command and url. Playwright starts the command when the readiness target is unavailable, waits for it, runs tests, and stops the process it launched.

What is the difference between Playwright webServer and baseURL?

webServer controls process startup and readiness. use.baseURL controls how relative browser URLs and supported network matchers are resolved. They normally use the same origin, although webServer.url may point to a health path.

Should reuseExistingServer be true in CI?

Usually no. Set it to !process.env.CI so local developers can reuse a running app while CI requires a clean process tied to the current build. This prevents stale servers from producing misleading results.

Can Playwright start multiple web servers?

Yes. Set webServer to an array of configuration objects, one for each frontend, API, proxy, or worker. Give each a unique name and readiness condition.

What is the default Playwright webServer timeout?

The default startup timeout is 60 seconds. Increase it based on measured cold-start behavior, but first confirm the command, working directory, environment, and readiness URL are correct.

Can webServer wait for console output instead of a URL?

Yes. Current Playwright configuration supports wait.stdout and wait.stderr regular expressions. It is useful for non-HTTP processes, while an application-level URL remains the stronger signal for web apps.

How do I see webServer logs?

Set stdout and stderr to pipe for the configured server and run with DEBUG=pw:webserver for Playwright lifecycle diagnostics. Named server entries make multi-process output easier to identify.

How do I use Playwright webServer with HTTPS?

Set the HTTPS readiness URL and, for a controlled untrusted local certificate, enable ignoreHTTPSErrors for both webServer readiness and browser use. Do not bypass certificate validation for production monitoring.

Related Guides