Selenium Manager and Driver Management
How Selenium Manager finds or downloads the right browser driver (and browser), how to pin versions, configure caching, proxies and offline mode, and what to do when it fails in CI.
Before Selenium 4.6, every tutorial started with “download chromedriver, put it on your PATH, and make sure the version matches Chrome.” Chrome updates every two weeks now, so that advice was a maintenance treadmill. Selenium Manager ended it. It is a small Rust binary bundled inside every Selenium 4.6+ release that resolves drivers automatically, and since 4.11 it can fetch browsers too.
What Selenium Manager Does
When you create a driver without specifying a driver path, the binding runs Selenium Manager, which:
- Detects the browser you asked for and its installed version (it checks the standard install locations, the PATH, and the
binaryyou set in options). - Looks up the matching driver version from the vendor’s endpoints (Chrome for Testing for chromedriver, GitHub releases for geckodriver, Microsoft for msedgedriver).
- Downloads the driver into a local cache (
~/.cache/seleniumby default) if it is not already there. - Returns the driver path to the binding, which starts it.
If no browser is installed, or you request a version that is not installed, Selenium Manager downloads a matching browser build into the same cache. Chrome comes from Chrome for Testing, Firefox from Mozilla’s archive, Edge from Microsoft.
The process is transparent. Your code is just:
WebDriver chrome = new ChromeDriver();WebDriver firefox = new FirefoxDriver();WebDriver edge = new EdgeDriver();from selenium import webdriver
chrome = webdriver.Chrome()firefox = webdriver.Firefox()edge = webdriver.Edge()const { Builder, Browser } = require('selenium-webdriver');
const chrome = await new Builder().forBrowser(Browser.CHROME).build();const firefox = await new Builder().forBrowser(Browser.FIREFOX).build();const edge = await new Builder().forBrowser(Browser.EDGE).build();IWebDriver chrome = new ChromeDriver();IWebDriver firefox = new FirefoxDriver();IWebDriver edge = new EdgeDriver();Pinning a Browser Version
Reproducible CI runs need a fixed browser version. Set browserVersion and Selenium Manager downloads that exact build (Chrome for Testing publishes every stable, beta, dev and canary). Milestone numbers like 128 and channel names like beta both work.
ChromeOptions options = new ChromeOptions();options.setBrowserVersion("128"); // or "stable", "beta", "dev", "canary"WebDriver driver = new ChromeDriver(options);
FirefoxOptions ff = new FirefoxOptions();ff.setBrowserVersion("esr"); // Firefox ESR channelWebDriver firefox = new FirefoxDriver(ff);options = webdriver.ChromeOptions()options.browser_version = "128" # or "stable", "beta", "dev", "canary"driver = webdriver.Chrome(options=options)
ff = webdriver.FirefoxOptions()ff.browser_version = "esr"firefox = webdriver.Firefox(options=ff)const chrome = require('selenium-webdriver/chrome');const firefox = require('selenium-webdriver/firefox');
const driver = await new Builder().forBrowser(Browser.CHROME).setChromeOptions(new chrome.Options().setBrowserVersion('128')).build();
const ff = await new Builder().forBrowser(Browser.FIREFOX).setFirefoxOptions(new firefox.Options().setBrowserVersion('esr')).build();var options = new ChromeOptions { BrowserVersion = "128" };IWebDriver driver = new ChromeDriver(options);
var ff = new FirefoxOptions { BrowserVersion = "esr" };IWebDriver firefox = new FirefoxDriver(ff);A word of caution from the Selenium team’s own telemetry: in 2025 they found over 28 million Selenium Manager users pinned to a single Chrome build from mid-2024, almost certainly CI pipelines nobody revisited. Pin for reproducibility, but schedule a review so you are not testing against a browser your users abandoned a year ago.
Configuration: Environment Variables, Config File, CLI
Selenium Manager reads settings from three places, in increasing priority: a config file, environment variables, and CLI flags (the bindings pass flags for you based on your options).
| Setting | Env var | Config key | Purpose |
|---|---|---|---|
| Cache directory | SE_CACHE_PATH | cache-path | Where drivers and browsers are stored |
| Proxy | SE_PROXY | proxy | HTTP(S) proxy for downloads, e.g. https://user:pass@proxy:8080 |
| Offline mode | SE_OFFLINE | offline | Never download; use only the cache |
| Force browser download | SE_FORCE_BROWSER_DOWNLOAD | force-browser-download | Ignore installed browsers |
| Avoid browser download | SE_AVOID_BROWSER_DOWNLOAD | avoid-browser-download | Fail instead of downloading a browser |
| Timeout | SE_TIMEOUT | timeout | Download timeout in seconds |
| Driver TTL | SE_DRIVER_TTL | driver-ttl | Seconds before re-checking for a newer driver |
| Browser TTL | SE_BROWSER_TTL | browser-ttl | Same, for browsers |
| Language binding | SE_LANGUAGE_BINDING | language-binding | Set automatically by the binding |
| Telemetry opt-out | SE_AVOID_STATS / DO_NOT_TRACK | avoid-stats | Disable anonymous usage stats (4.48 honours DO_NOT_TRACK) |
The config file lives at ~/.cache/selenium/se-config.toml:
proxy = "https://proxy.corp.example:8080"timeout = 600driver-ttl = 86400You can also run the binary directly to debug what it would do. It ships inside the binding package; on the Python side it is under selenium/webdriver/common/<platform>/selenium-manager.
# What driver would Selenium Manager use for the installed Chrome?selenium-manager --browser chrome --debug
# Resolve and download Chrome 128 plus its driver into the cacheselenium-manager --browser chrome --browser-version 128 --cache-path /opt/se-cache
# Output as JSON for scriptingselenium-manager --browser firefox --output jsonSince Selenium 4.44 a broader selenium CLI tool is being introduced that wraps Selenium Manager, Grid and other utilities in one binary. It is early; the Manager flags above remain the stable interface.
CI and Air-Gapped Environments
Selenium Manager needs network access to download. In a locked-down pipeline you have three options:
- Warm the cache in the image build step, then run tests with
SE_OFFLINE=trueso any cache miss fails fast instead of hanging on a blocked download. - Use the official Selenium Docker images (
selenium/standalone-chrome, mirrored onghcr.io/seleniumhq) which ship browser and driver pre-matched. Your tests then connect to a Grid URL and Selenium Manager never runs. - Point at an explicit driver you ship with the image; Selenium Manager is bypassed entirely when a driver path is supplied.
ChromeDriverService service = new ChromeDriverService.Builder() .usingDriverExecutable(new File("/opt/drivers/chromedriver")) .build();WebDriver driver = new ChromeDriver(service, new ChromeOptions());from selenium.webdriver.chrome.service import Service
service = Service(executable_path="/opt/drivers/chromedriver")driver = webdriver.Chrome(service=service)const chrome = require('selenium-webdriver/chrome');
const service = new chrome.ServiceBuilder('/opt/drivers/chromedriver');const driver = await new Builder().forBrowser(Browser.CHROME).setChromeService(service).build();var service = ChromeDriverService.CreateDefaultService("/opt/drivers", "chromedriver");IWebDriver driver = new ChromeDriver(service, new ChromeOptions());A GitHub Actions job that warms the cache once per workflow:
- name: Warm Selenium Manager cache run: | python -c "from selenium import webdriver; d = webdriver.Chrome(); d.quit()" env: SE_CACHE_PATH: ${{ runner.temp }}/se-cache- name: Run tests offline run: pytest tests/ env: SE_CACHE_PATH: ${{ runner.temp }}/se-cache SE_OFFLINE: "true"Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
SessionNotCreatedException: This version of ChromeDriver only supports Chrome version X | You supplied an old driver path, so Selenium Manager did not run | Remove the explicit path or update the driver |
| Long hang on first driver creation | Download blocked by firewall or proxy | Set SE_PROXY; or warm the cache and use SE_OFFLINE |
Unable to obtain driver for chrome with --debug showing no browser found | Browser installed in a non-standard location | Set options.setBinary(...) / binary_location, or let Manager download the browser |
| Tests suddenly use a different Chrome | Manager found a newer installed Chrome | Pin browserVersion in CI |
| Works locally, fails in Docker | Missing system libraries for the downloaded browser | Use the official Selenium images or install Chrome’s dependencies in your image |
Enable debug logging to see every decision: --debug on the CLI, or set the binding’s logger to DEBUG (Python: logging.getLogger('selenium').setLevel(logging.DEBUG)).
Summary
- Selenium Manager (4.6+) resolves drivers and, from 4.11, browsers, into a local cache. You stop managing chromedriver.
- Pin
browserVersionin CI for reproducibility, and review the pin regularly. - Configure through
SE_*environment variables orse-config.toml; useSE_OFFLINEafter warming the cache in restricted networks. - Supplying an explicit driver path bypasses Manager entirely, which is also how you get old driver mismatch errors back.