Skip to main content
SeleniumDecoded

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.

Selenium 4 Stable Updated 9 Sept 2026 · Verified against Selenium 4.48.0

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:

  1. Detects the browser you asked for and its installed version (it checks the standard install locations, the PATH, and the binary you set in options).
  2. Looks up the matching driver version from the vendor’s endpoints (Chrome for Testing for chromedriver, GitHub releases for geckodriver, Microsoft for msedgedriver).
  3. Downloads the driver into a local cache (~/.cache/selenium by default) if it is not already there.
  4. 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:

Drivers resolved automatically
Selenium 4 Stable
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.

Request a specific browser build
Selenium 4 Stable
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 channel
WebDriver 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).

SettingEnv varConfig keyPurpose
Cache directorySE_CACHE_PATHcache-pathWhere drivers and browsers are stored
ProxySE_PROXYproxyHTTP(S) proxy for downloads, e.g. https://user:pass@proxy:8080
Offline modeSE_OFFLINEofflineNever download; use only the cache
Force browser downloadSE_FORCE_BROWSER_DOWNLOADforce-browser-downloadIgnore installed browsers
Avoid browser downloadSE_AVOID_BROWSER_DOWNLOADavoid-browser-downloadFail instead of downloading a browser
TimeoutSE_TIMEOUTtimeoutDownload timeout in seconds
Driver TTLSE_DRIVER_TTLdriver-ttlSeconds before re-checking for a newer driver
Browser TTLSE_BROWSER_TTLbrowser-ttlSame, for browsers
Language bindingSE_LANGUAGE_BINDINGlanguage-bindingSet automatically by the binding
Telemetry opt-outSE_AVOID_STATS / DO_NOT_TRACKavoid-statsDisable anonymous usage stats (4.48 honours DO_NOT_TRACK)

The config file lives at ~/.cache/selenium/se-config.toml:

~/.cache/selenium/se-config.toml
proxy = "https://proxy.corp.example:8080"
timeout = 600
driver-ttl = 86400

You 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.

Terminal window
# 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 cache
selenium-manager --browser chrome --browser-version 128 --cache-path /opt/se-cache
# Output as JSON for scripting
selenium-manager --browser firefox --output json

Since 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:

  1. Warm the cache in the image build step, then run tests with SE_OFFLINE=true so any cache miss fails fast instead of hanging on a blocked download.
  2. Use the official Selenium Docker images (selenium/standalone-chrome, mirrored on ghcr.io/seleniumhq) which ship browser and driver pre-matched. Your tests then connect to a Grid URL and Selenium Manager never runs.
  3. Point at an explicit driver you ship with the image; Selenium Manager is bypassed entirely when a driver path is supplied.
Bypassing Selenium Manager with an explicit driver
Selenium 4 Stable
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

SymptomLikely causeFix
SessionNotCreatedException: This version of ChromeDriver only supports Chrome version XYou supplied an old driver path, so Selenium Manager did not runRemove the explicit path or update the driver
Long hang on first driver creationDownload blocked by firewall or proxySet SE_PROXY; or warm the cache and use SE_OFFLINE
Unable to obtain driver for chrome with --debug showing no browser foundBrowser installed in a non-standard locationSet options.setBinary(...) / binary_location, or let Manager download the browser
Tests suddenly use a different ChromeManager found a newer installed ChromePin browserVersion in CI
Works locally, fails in DockerMissing system libraries for the downloaded browserUse 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 browserVersion in CI for reproducibility, and review the pin regularly.
  • Configure through SE_* environment variables or se-config.toml; use SE_OFFLINE after 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.

Related lessons