# How to Use Persistent Browser Sessions with `persistent_local_browser` in Webwright

> Learn to use persistent browser sessions with Webwright's persistent_local_browser tool. Reconnect across scripts with a detached Chromium process and reusable CDP endpoint.

- Repository: [Microsoft/Webwright](https://github.com/microsoft/Webwright)
- Tags: how-to-guide
- Published: 2026-06-25

---

**The `persistent_local_browser` tool in Webwright launches a detached Chromium process with a private user-data directory and reusable CDP endpoint, storing session metadata in a JSON file that enables reconnecting across multiple scripts or CI steps.**

Webwright, Microsoft's open-source automation framework, provides a command-line tool called **`persistent_local_browser`** that solves the problem of maintaining browser state across disconnected execution steps. Unlike standard ephemeral browser instances that terminate when your script ends, this tool creates a long-living Chromium process that survives the parent Python interpreter and can be reattached via Chrome DevTools Protocol (CDP).

## Understanding the Architecture

The tool is implemented in [`src/webwright/tools/persistent_local_browser.py`](https://github.com/microsoft/Webwright/blob/main/src/webwright/tools/persistent_local_browser.py) and manages browser persistence through three core mechanisms: process detachment, session serialization, and lifecycle commands.

### Process Detachment and Survival

When you invoke the `create` command, Webwright launches Chromium using `start_new_session=True` on POSIX systems (📄[line 41‑44]), placing the browser in a new process group. This architectural decision ensures the Chromium process survives even if the spawning shell terminates or the parent Python process crashes. The browser launches with `--remote-debugging-port=0`, causing Chromium to select an available port and print a line matching the `_DEVTOOLS_RE` pattern (📄[line 43]).

### Session State Storage

The tool captures the DevTools WebSocket URL and writes an atomic JSON record (via `out_path.write_text()`, 📄[line 163‑164]) containing:

- **session ID**: Unique identifier for the session
- **connectUrl**: The CDP WebSocket endpoint (e.g., `ws://127.0.0.1:XXXXX/devtools/browser/<id>`)
- **pid**: Process ID for liveness checks
- **userDataDir**: Path to the isolated user-data directory
- **executable**: Path to the Playwright-bundled Chromium binary

By default, this file is named [`.lb_session.json`](https://github.com/microsoft/Webwright/blob/main/.lb_session.json) and resides in your specified workspace directory.

### Three-Command Lifecycle

The tool exposes three sub-commands implemented as distinct functions:

- **`create`**: Launches Chromium via `_chromium_executable()` (📄[line 54‑66]), waits for the DevTools URL using `_wait_for_devtools_url()` (📄[line 81‑104]), and writes the session file
- **`info`**: Validates session health by checking `_pid_alive()` (📄[line 69‑78]) and displays enriched session metadata
- **`release`**: Terminates the browser via `_terminate_pid()` (📄[line 83‑100]) using SIGTERM followed by SIGKILL if necessary, with optional cleanup of user data

## Creating a Persistent Browser Session

To create a session, Webwright resolves paths relative to your workspace using `_resolve_path()` (📄[line 46‑51]) and constructs launch arguments (📄[line 118‑130]) that include `--remote-debugging-port=0` and `--user-data-dir`.

```bash
python -m webwright.tools.persistent_local_browser create \
  --workspace-dir /path/to/workspace \
  --out .lb_session.json \
  --headless \
  --no-sandbox \
  --startup-timeout 30

```

The `--workspace-dir` parameter ensures all file paths (session file, user-data directory) resolve relative to a consistent base, critical for reproducible CI pipelines. The `--startup-timeout` defaults to 30 seconds but can be adjusted for slower environments.

Upon successful creation, the tool prints environment-variable-style helpers to stdout:

```text
LB_SESSION_ID=abc-123
LB_CONNECT_URL=ws://127.0.0.1:9222/devtools/browser/...
LB_USER_DATA_DIR=/path/to/workspace/.user_data

```

## Connecting to an Existing Session

Reuse the persistent session by reading the JSON file and connecting via Playwright's CDP transport. **Do not call `browser.close()`**—this would terminate the shared process. Instead, use `browser.disconnect()`.

```python
import json
from pathlib import Path
from playwright.sync_api import sync_playwright

session_path = Path("/path/to/workspace/.lb_session.json")
session = json.loads(session_path.read_text())

with sync_playwright() as p:
    # Connect to the existing CDP endpoint

    browser = p.chromium.connect_over_cdp(session["connectUrl"])
    
    page = browser.new_page()
    page.goto("https://example.com")
    # ... perform actions ...

    
    browser.disconnect()  # Detaches without killing Chromium

```

This pattern allows multiple independent scripts to share authentication state, cookies, and local storage because they all attach to the same underlying Chromium instance using the `--user-data-dir` specified in the session record.

## Managing and Releasing Sessions

Monitor session health before attempting reconnection to avoid errors from stale PIDs.

### Checking Session Status

The `info` command verifies process liveness and displays current metadata:

```bash
python -m webwright.tools.persistent_local_browser info \
  --workspace-dir /path/to/workspace \
  --session-file .lb_session.json

```

This executes `_pid_alive()` to check if the stored PID remains active in the process table, helping you identify zombie session files.

### Terminating and Cleaning Up

When finished, release the browser deterministically:

```bash
python -m webwright.tools.persistent_local_browser release \
  --workspace-dir /path/to/workspace \
  --session-file .lb_session.json \
  --delete-user-data \
  --delete-file

```

The `release` command first sends SIGTERM, waits gracefully, then escalates to SIGKILL via `_terminate_pid()` if the process refuses to exit. The `--delete-user-data` flag removes the temporary profile directory (`DEFAULT_USER_DATA_SUBDIR` as defined in [`src/webwright/config/persistent_browser.yaml`](https://github.com/microsoft/Webwright/blob/main/src/webwright/config/persistent_browser.yaml)), while `--delete-file` removes the session JSON itself.

## Configuration and Path Resolution

Webwright respects defaults from [`persistent_browser.yaml`](https://github.com/microsoft/Webwright/blob/main/persistent_browser.yaml) located in `src/webwright/config/`, including the default user-data subdirectory path referenced as `DEFAULT_USER_DATA_SUBDIR` (📄[line 42]).

The `_resolve_path()` utility ensures all file arguments are absolute paths relative to `--workspace-dir`, preventing path resolution errors when commands execute from different working directories. This is essential when moving between CI steps where `$PWD` may change between the `create` and `connect` phases.

## Summary

- **Process isolation**: The `persistent_local_browser` tool launches Chromium in a new session group, ensuring survival after parent process termination
- **CDP connection**: Stores WebSocket endpoint in [`.lb_session.json`](https://github.com/microsoft/Webwright/blob/main/.lb_session.json) for reconnection via `connect_over_cdp()`
- **Three-command workflow**: Use `create` to start, `info` to check health (`_pid_alive`), and `release` to terminate (`_terminate_pid`) and clean up
- **Safe reconnection**: Always use `browser.disconnect()` instead of `close()` when reusing persistent sessions
- **CI-friendly**: Path resolution relative to `--workspace-dir` and configurable startup timeouts accommodate containerized environments

## Frequently Asked Questions

### How does the browser survive after my script exits?

The tool uses `start_new_session=True` when spawning the Chromium process on POSIX systems, creating a new process group that is not a session leader. This detaches the browser from the parent shell's session, allowing it to persist even when the original Python process or Bash step terminates.

### Can I customize the Chromium executable used for persistent sessions?

Yes. The `_chromium_executable()` function (📄[line 54‑66]) locates the Playwright-bundled Chromium by default, but you can override the executable path via the `--executable` CLI argument when running the `create` command, or by configuring defaults in [`persistent_browser.yaml`](https://github.com/microsoft/Webwright/blob/main/persistent_browser.yaml).

### What happens if I try to connect to a dead session?

The `info` command checks process liveness using `_pid_alive()` (📄[line 69‑78]), which verifies the stored PID exists in the process table. If the browser has crashed or been killed externally, the command reports the session as inactive, allowing you to avoid connection errors by validating the session before calling `connect_over_cdp()`.

### How do I ensure the user data directory is cleaned up automatically?

Pass both `--delete-user-data` and `--delete-file` flags to the `release` command. The `--delete-user-data` flag triggers removal of the temporary profile directory created under your workspace, while `--delete-file` removes the [`.lb_session.json`](https://github.com/microsoft/Webwright/blob/main/.lb_session.json) record, ensuring no stale session files or disk space remain after teardown.