Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Replace Selenium Ruby’s deprecated driver_opts, driver_path, and port initializer options with a browser-specific Selenium::WebDriver::Service object. Put the driver executable, driver-process arguments, and port on the service; keep browser switches such as --headless in the browser Options object.
The migration applies the same separation to Chrome, Firefox, and Edge. A minimal Chrome conversion is:
service = Selenium::WebDriver::Service.chrome
service.executable_path = '/path/to/chromedriver'
service.port = 9515
service.args << '--log-level=0'
options = Selenium::WebDriver::Options.chrome
options.add_argument('--headless')
driver = Selenium::WebDriver.for(:chrome, service: service, options: options)
Why Selenium changed the initializer
Selenium Ruby’s changelog marks passing driver_opts, driver_path, and port directly to the driver initializer as deprecated. The replacement is a browser-specific Service class. Selenium’s documentation describes Service classes as responsible for managing the starting and stopping of local drivers.
This is more than a renamed hash key. It separates two processes:
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
- The driver service: the executable Selenium starts, its listening port, and arguments consumed by the driver process.
- The browser session: browser capabilities, preferences, and command-line switches supplied through an
Optionsobject.
Use service: and options: together when creating the session. Do not move every old option into one object.
Before and after: Chrome
Deprecated initializer
driver = Selenium::WebDriver.for :chrome,
driver_opts: { args: ['--log-level=0'] },
driver_path: '/path/to/chromedriver',
port: 9515
Here, the executable path and port configure the driver process. The args entry is also driver-process configuration in this example, not a browser capability.
Supported Service-based initializer
service = Selenium::WebDriver::Service.chrome
service.executable_path = '/path/to/chromedriver'
service.port = 9515
service.args << '--log-level=0'
options = Selenium::WebDriver::Options.chrome
options.add_argument('--headless')
driver = Selenium::WebDriver.for(:chrome, service: service, options: options)
service.executable_path replaces driver_path; service.port replaces port; and driver-process arguments are added to service.args. The browser’s headless switch remains in options.
Where each old setting goes
| Deprecated setting | Replacement | What it controls |
|---|---|---|
driver_path |
service.executable_path |
The local driver executable, when you need to specify it explicitly |
port |
service.port |
The port on which the local driver service listens |
driver_opts driver arguments |
service.args (or service constructor arguments) |
Command-line arguments consumed by the driver process |
Browser flags such as --headless |
options.add_argument |
Arguments passed to the browser session |
| Browser capabilities and preferences | The browser-specific Options object |
Session capabilities, preferences, and browser behavior |
The important diagnostic question is: “Does this switch configure the driver executable, or does it configure the browser?” Driver executable settings belong to Service; browser behavior belongs to Options.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Chrome migration step by step
- Create the Service: call
Selenium::WebDriver::Service.chrome. - Set an executable only when required: assign the full path to
service.executable_path. If your environment already resolves the driver, omit this assignment. - Choose a port when required: assign an available integer to
service.port. Omit it when Selenium should choose its normal behavior. - Move driver arguments: append each driver-process argument to
service.args. - Create browser options: use
Selenium::WebDriver::Options.chromeand add browser switches, preferences, and capabilities there. - Start the session: pass both objects to
Selenium::WebDriver.for(:chrome, service: service, options: options).
A reusable Ruby method
require 'selenium-webdriver'
def build_chrome_driver(driver_path: nil, port: nil, headless: false)
service = Selenium::WebDriver::Service.chrome
service.executable_path = driver_path if driver_path
service.port = port if port
service.args << '--log-level=0'
options = Selenium::WebDriver::Options.chrome
options.add_argument('--headless') if headless
Selenium::WebDriver.for(:chrome, service: service, options: options)
end
driver = build_chrome_driver(
driver_path: '/path/to/chromedriver',
port: 9515,
headless: true
)
begin
driver.get('https://example.com')
puts driver.title
ensure
driver.quit
end
The ensure block matters in test runners and scripts: it closes the browser and lets Selenium stop the local service even when navigation or an assertion fails.
Firefox and Edge equivalents
Firefox
service = Selenium::WebDriver::Service.firefox
service.executable_path = '/path/to/geckodriver'
service.port = 4444
service.args << '--log=debug'
options = Selenium::WebDriver::Options.firefox
options.add_argument('-headless')
driver = Selenium::WebDriver.for(:firefox, service: service, options: options)
Use the Firefox Service factory for the Firefox driver executable. Firefox browser arguments stay on Firefox Options.
Rank #2
Edge
service = Selenium::WebDriver::Service.edge
service.executable_path = '/path/to/msedgedriver'
service.port = 17556
service.args << '--verbose'
options = Selenium::WebDriver::Options.edge
options.add_argument('--headless')
driver = Selenium::WebDriver.for(:edge, service: service, options: options)
Edge follows the same split: Edge driver process settings on the Edge Service, Edge browser settings on Edge Options.
Handling paths, ports, and arguments safely
Executable paths
Use an absolute path when a machine has multiple driver versions or when the driver is not on the process PATH. Verify that the file exists and is executable under the account running the test. A path that works in an interactive shell may fail in CI because the runner uses a different user or environment.
Ports
A fixed port is useful when another process must connect to the driver, but it creates a collision risk when tests run in parallel. Give each concurrent process a distinct available port, or omit the assignment if a fixed port is not part of your integration contract. A “port already in use” error is a service-startup problem, not a browser option problem.
Service arguments versus browser arguments
Arguments such as driver logging controls belong in service.args. A switch that changes Chrome, Firefox, or Edge itself belongs in that browser’s Options object. If a browser flag is placed on the Service, the driver may ignore it or reject it because the driver process does not understand browser-only arguments.
Common migration failures and fixes
Deprecation warnings remain
Cause: one code path still passes driver_opts, driver_path, or port to Selenium::WebDriver.for.
Fix: search factories, shared test helpers, and environment-specific branches. Construct the Service in each browser-specific branch and pass it with the service: keyword.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Rank #3
“Executable not found” or service cannot start
Cause: service.executable_path points to a missing file, the path is relative to an unexpected working directory, or the runner lacks execute permission.
Fix: use an absolute path, check permissions as the test user, and confirm the driver matches the browser installed in that environment. If automatic driver resolution is configured, remove an obsolete hard-coded path rather than pointing at a stale binary.
“Address already in use”
Cause: another driver or test process owns service.port.
Fix: stop the orphaned process, choose a free port, or avoid hard-coding the port for parallel tests. Ensure every test calls driver.quit in an ensure block.
Recommended Free Tools
Headless mode is ignored
Cause: --headless was moved to service.args.
Fix: put it on the browser options object, for example options = Selenium::WebDriver::Options.chrome followed by options.add_argument('--headless').
The driver starts but a browser capability has no effect
Cause: a browser preference or capability was treated as a driver-process argument.
Fix: configure it through the relevant Options object. Service is only for managing the local driver process.
Tests pass locally but fail in CI
Cause: differences in installed browser, driver, filesystem path, permissions, user, or available ports.
Fix: log the effective environment, use paths visible to the CI account, avoid assumptions about the working directory, and test the chosen port and executable before creating the session. The API shape is documented, but runtime results still depend on the Ruby gem, browser, driver, and local environment versions.
Verification checklist
- The browser-specific Service factory is used: Chrome, Firefox, or Edge.
- No deprecated initializer keys remain.
driver_pathis nowservice.executable_path, if an explicit path is needed.portis nowservice.port, if a fixed port is required.- Driver-process arguments are on
service.args. - Browser switches, preferences, and capabilities are on Options.
- The session call includes both
service: serviceandoptions: options. - The test quits the driver in cleanup code.
- The target machine’s browser, driver, Ruby gem, path, permissions, and port have been checked.
Performance, reliability, and parallel-test considerations
Service configuration does not make navigation faster by itself; it controls how the local driver process is launched. Reliability comes from making that launch deterministic: use a known executable when necessary, avoid port collisions, keep browser flags in Options, and always clean up sessions.
For parallel suites, do not give every worker the same fixed port. A worker-specific port or Selenium’s normal port-selection behavior avoids two services competing for one listener. Likewise, avoid a single shared driver executable path that only exists on one workstation; package or provision the driver consistently for every runner.
When diagnosing startup failures, separate the stages: first confirm the Service can launch, then confirm the browser session can be created, and only afterward investigate page navigation or application assertions. This prevents a browser capability problem from being mistaken for a driver executable problem.
Best Value
Or skip the browser setup
If your goal is simply to obtain a clean screenshot or PDF rather than drive an interactive browser, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
See the ScreenshotNeo API documentation for authentication and options. This cURL request captures Stripe as WebP:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Every plan includes full-page capture, element selection, device and viewport controls, retina scale, PDF settings, custom CSS and JavaScript, waits, request blocking, headers, cookies, geolocation, caching, signed links, asynchronous jobs, bulk capture, usage data, and an OpenAPI specification. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up free for ScreenshotNeo.
Frequently Asked Questions
Can I still pass a custom ChromeDriver path?
Yes. Assign the absolute path to service.executable_path on a Chrome Service object, then pass that object with service:.
Should every argument from driver_opts move to service.args?
No. Move driver-process arguments to service.args; browser switches, preferences, and capabilities belong in the browser’s Options object.
Do Firefox and Edge use the same migration pattern?
Yes. Use Selenium::WebDriver::Service.firefox or Service.edge, with the corresponding Options object.
Is a fixed port required?
No. Set service.port only when your environment requires a known port. Fixed ports need coordination in parallel runs.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

