How to Configure user_data_dir for Persistent Browser Profiles in Zendriver
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 and zendriver/core/browser.py.
The Config Class
In 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, 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.
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.
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, each Browser.create() call receives a distinct temporary directory, preventing profile collision bugs during concurrent execution.
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.
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
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
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
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
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 viatemp_profile_dir()inzendriver/core/config.py, cleaned up automatically whenbrowser.stop()executes. - Persistent profiles: Providing a path to
user_data_dirsets_custom_data_dir = True, which prevents_cleanup_temporary_profile()inzendriver/core/browser.pyfrom deleting the folder. - Concurrent isolation: Multiple browsers instantiated from the same default
Configreceive distinct temporary directories, avoiding profile collisions. - State persistence: Reusing the same custom
user_data_dirpath across separateConfiginstances 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 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 during shutdown, provided no custom user_data_dir was specified.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →