# How to Configure user_data_dir for Persistent Browser Profiles in Zendriver

> Learn how to configure user_data_dir in Zendriver for persistent browser profiles. Save cookies, extensions, and login states across sessions.

- Repository: [CDP Driver/zendriver](https://github.com/cdpdriver/zendriver)
- Tags: how-to-guide
- Published: 2026-02-27

---

**The `user_data_dir` parameter in zendriver controls whether Chrome launches with a temporary auto-generated profile or a persistent custom directory, determining if cookies, extensions, and login states survive between browser sessions.**

Zendriver provides flexible profile management through the `user_data_dir` configuration option. This parameter dictates where Chrome stores user data including cookies, local storage, and extensions. Understanding how zendriver handles this directory is essential for automation workflows that require state persistence or complete isolation between browser instances.

## Understanding user_data_dir in Zendriver

The profile directory lifecycle is managed cooperatively by two core components: the `Config` class, which defines the path and persistence behavior, and the `Browser` class, which executes the cleanup logic. According to the zendriver source code, the implementation splits responsibility between [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py) and [`zendriver/core/browser.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/browser.py).

### The Config Class

In [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py), the `Config` class exposes `user_data_dir` as a property that lazily initializes temporary storage. When you access `config.user_data_dir` without previously setting it, the property invokes `temp_profile_dir()` to generate a unique temporary folder path (typically under `/tmp/` with a `uc_` prefix).

Setting the attribute manually triggers an internal flag: the setter assigns `True` to `_custom_data_dir`. This flag drives the `uses_custom_data_dir` property, which the browser queries to decide whether the directory is eligible for automatic deletion. If you supplied a custom path, zendriver treats the directory as persistent and preserves it after shutdown.

### The Browser Class

In [`zendriver/core/browser.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/browser.py), the `Browser` class consumes the configuration during startup. The `start()` method appends `--user-data-dir={path}` to the Chrome launch arguments, directing the browser engine to the specified location. When `browser.stop()` executes, the internal `_cleanup_temporary_profile()` method checks `config.uses_custom_data_dir`. If this returns `False` (indicating an auto-generated temporary profile), the method recursively deletes the folder. If `True`, the cleanup routine skips deletion, leaving your custom profile intact on disk.

## Configuration Patterns

Zendriver supports four primary patterns for managing browser profiles, each suited to different automation requirements.

### Auto-Generated Temporary Profiles

By default, instantiating `Config()` without arguments creates an ephemeral profile. This pattern ensures complete isolation—every browser session starts with a fresh state, and no disk space accumulates after `stop()` executes.

```python
import zendriver as zd

cfg = zd.Config()
browser = await zd.start(cfg)
print("Profile location:", browser.config.user_data_dir)

# Output: /tmp/uc_8f3a2b (example path)

await browser.stop()

# Directory /tmp/uc_8f3a2b is automatically removed

```

### Persistent Custom Profiles

Supplying a path to the `user_data_dir` parameter creates a reusable profile. This is critical for workflows requiring authentication persistence, installed extensions, or specific browser settings across multiple runs.

```python
import zendriver as zd
import os

profile_path = os.path.expanduser("~/.zendriver_profiles/production")
cfg = zd.Config(user_data_dir=profile_path, headless=False)
browser = await zd.start(cfg)

# Navigate and authenticate

await browser.get("https://example.com/login")

# ... perform login actions ...

await browser.stop()

# ~/.zendriver_profiles/production remains on disk with cookies intact

```

Because the `Config` constructor receives a explicit path, `uses_custom_data_dir` evaluates to `True`, and the cleanup routine preserves your data.

### Isolated Profiles for Concurrent Browsers

When launching multiple browser instances from the same base `Config` without a custom `user_data_dir`, zendriver guarantees isolation. As confirmed in [`tests/core/test_multiple_browsers.py`](https://github.com/cdpdriver/zendriver/blob/main/tests/core/test_multiple_browsers.py), each `Browser.create()` call receives a distinct temporary directory, preventing profile collision bugs during concurrent execution.

```python
import zendriver as zd

base_cfg = zd.Config()

browser_a = await zd.start(base_cfg)
browser_b = await zd.start(base_cfg)
browser_c = await zd.start(base_cfg)

# Each browser operates in its own isolated environment

print("A:", browser_a.config.user_data_dir)  # /tmp/uc_abc123

print("B:", browser_b.config.user_data_dir)  # /tmp/uc_def456

print("C:", browser_c.config.user_data_dir)  # /tmp/uc_ghi789

await browser_a.stop()
await browser_b.stop()
await browser_c.stop()

```

### Sharing Profiles Across Multiple Runs

To maintain state between distinct Python executions, reuse the same absolute path across separate `Config` instances. This pattern preserves cookies, local storage, and extension data between automation scripts.

```python
import zendriver as zd

shared_path = "/tmp/shared_automation_profile"

# First execution

cfg1 = zd.Config(user_data_dir=shared_path)
browser1 = await zd.start(cfg1)
await browser1.get("https://example.com")
await browser1.stop()

# Second execution (hours or days later)

cfg2 = zd.Config(user_data_dir=shared_path)
browser2 = await zd.start(cfg2)

# Browser2 retains all cookies and storage from browser1's session

await browser2.stop()

```

## Code Examples

### Launching with a Temporary Profile

```python
import zendriver as zd

config = zd.Config()
browser = await zd.start(config)
print("Temporary profile:", browser.config.user_data_dir)

await browser.get("https://example.com")
await browser.stop()  # Auto-cleanup removes the temp directory

```

### Launching with a Persistent Custom Profile

```python
import zendriver as zd

custom_dir = "/home/user/chrome_profiles/persistent"
config = zd.Config(user_data_dir=custom_dir)
browser = await zd.start(config)

# Profile persists after stop()

await browser.stop()

```

### Launching Multiple Isolated Browsers

```python
import zendriver as zd

base = zd.Config()

b1 = await zd.start(base)
b2 = await zd.start(base)

assert b1.config.user_data_dir != b2.config.user_data_dir
print(f"Browser 1: {b1.config.user_data_dir}")
print(f"Browser 2: {b2.config.user_data_dir}")

await b1.stop()
await b2.stop()

```

### Reusing a Profile for Stateful Automation

```python
import zendriver as zd

profile = "/tmp/automation_state"

# Run 1: Establish session

b1 = await zd.start(zd.Config(user_data_dir=profile))
await b1.get("https://example.com/login")
await b1.stop()

# Run 2: Resume with existing cookies

b2 = await zd.start(zd.Config(user_data_dir=profile))
await b2.get("https://example.com/dashboard")  # Still authenticated

await b2.stop()

```

## Summary

- **Auto-generated profiles**: Calling `Config()` without arguments creates a unique temporary directory via `temp_profile_dir()` in [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py), cleaned up automatically when `browser.stop()` executes.
- **Persistent profiles**: Providing a path to `user_data_dir` sets `_custom_data_dir = True`, which prevents `_cleanup_temporary_profile()` in [`zendriver/core/browser.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/browser.py) from deleting the folder.
- **Concurrent isolation**: Multiple browsers instantiated from the same default `Config` receive distinct temporary directories, avoiding profile collisions.
- **State persistence**: Reusing the same custom `user_data_dir` path across separate `Config` instances preserves cookies, extensions, and storage between browser sessions.

## Frequently Asked Questions

### What happens if I don't specify a user_data_dir?

Zendriver automatically generates a unique temporary directory under your system's temp folder (typically `/tmp/uc_<random_string>` on Unix systems). The `Config` class lazily creates this path when you first access `user_data_dir`, and the `Browser` class automatically deletes the folder when you call `stop()`, ensuring no persistent data remains on disk.

### How do I prevent zendriver from deleting my browser profile?

Explicitly set the `user_data_dir` parameter when constructing your `Config` object. This action sets the internal `_custom_data_dir` flag to `True`, causing `uses_custom_data_dir` to return `True`. When `browser.stop()` triggers `_cleanup_temporary_profile()`, it checks this flag and skips deletion, leaving your custom directory intact for future sessions.

### Can multiple browsers share the same user_data_dir simultaneously?

No. Chrome's architecture locks the user data directory exclusively to a single process. Attempting to launch two `Browser` instances pointing to the same `user_data_dir` simultaneously will cause the second launch to fail with a profile lock error. To run concurrent browsers, either use separate custom directories or rely on the default temporary profile behavior, which automatically generates isolated directories for each instance.

### Where does zendriver store temporary browser profiles?

The `temp_profile_dir()` function in [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py) generates paths using Python's `tempfile` utilities, typically placing them in the system's temporary directory with a `uc_` prefix (e.g., `/tmp/uc_8f3a2b`). These directories exist only for the lifespan of the `Browser` instance and are removed by `_cleanup_temporary_profile()` in [`zendriver/core/browser.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/browser.py) during shutdown, provided no custom `user_data_dir` was specified.