How to Capture Selenium Screenshots in a Docker Container with Headless Chrome
Run headless Chrome in Docker, capture Selenium screenshots, and solve common version, shared-memory, viewport, and CI artifact issues.
Use Selenium WebDriver to open the page and call driver.save_screenshot('screenshot.png'). In Docker, make the browser version predictable with a pinned Selenium image tag, give Chrome adequate shared memory, set the viewport explicitly, and save the screenshot on the machine where the WebDriver test process runs.
1. Run headless Chrome with Selenium in Docker
This example assumes Python Selenium connects to Selenium’s official standalone Chrome container. The browser runs in the container; the Python process receives the screenshot through WebDriver and writes it to its own filesystem.
Start the browser container
docker run --rm -p 4444:4444 --shm-size=2g \
selenium/standalone-chrome:4.48.0-20260905
The tag is an example from the Selenium project README reviewed for this guide. Choose a tag suitable for your environment and pin the full tag so browser and Grid versions do not drift. Selenium recommends starting with --shm-size=2g to help avoid browser crashes; treat that as a workload-dependent starting point, not a universal minimum. See the docker-selenium README.
Install the Python binding
python -m pip install selenium
Connect, capture, and close the session
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless=new')
# The WebDriver endpoint is exposed by the standalone container.
driver = webdriver.Remote(
command_executor='http://127.0.0.1:4444/wd/hub',
options=options,
)
try:
driver.set_window_size(1440, 1000)
driver.get('https://example.com')
driver.save_screenshot('screenshot.png')
finally:
driver.quit()
save_screenshot captures the current browsing context and saves it at the specified path. In this remote setup, the screenshot path belongs to the Python test process, not the browser container. Ensure the process can write to that path and configure CI artifact collection to pick up that same location. Selenium’s Chrome documentation covers Chrome options and screenshot usage.
2. Choose the right capture and dimensions
WebDriver screenshots are useful when the page is already open in a Selenium session or the test must interact with it. The standard page screenshot captures the current browsing context; do not assume it produces a full-page image. Selenium also supports taking a screenshot of an element when only a specific component is needed.
| Need | Approach | Consideration |
|---|---|---|
| Current page view | driver.save_screenshot(path) |
Set the browser window size first for repeatable viewport dimensions. |
| One component | Find the element and call its screenshot method | The element must be present and visible in the current page state. |
| Screenshot without WebDriver interaction | Chrome headless CLI --screenshot |
Use when a standalone browser command is enough; it is a separate workflow. |
For a repeatable viewport capture, set and record the window dimensions in the test. Chrome’s headless CLI also supports an explicit window size. For example:
chrome --headless=new --screenshot --window-size=412,892 https://developer.chrome.com/
See Chrome Headless documentation for the command-line options. The CLI and WebDriver approaches have different control and interaction models, so verify the output dimensions and composition for the workflow you choose.
3. Keep browser versions and headless settings compatible
Selenium 4 supports Chrome v75 and later, and Chrome and ChromeDriver need matching major versions. With docker-selenium, pinning a full image tag helps keep the packaged browser and Grid combination stable. When startup fails after changing browser or image versions, check both the selected image tag and the browser-driver compatibility.
Headless and Xvfb behavior can change with Chrome and docker-selenium releases. The current docker-selenium README reviewed for this guide describes version-specific SE_START_XVFB=true guidance for newer Chrome/Chromium versions: for --headless=new at v127+, and for --headless at v132+. Check the README matching the exact image tag you run; do not copy these version conditions blindly to another release. See the Selenium Chrome docs and docker-selenium README.
4. Common errors and fixes
| Symptom | Likely cause | What to check or change |
|---|---|---|
| Chrome exits, crashes, or the session disappears | Insufficient shared memory for the browser workload | Increase Docker shared memory. Selenium suggests --shm-size=2g as a starting point, then tune based on workload. |
| Session creation fails with a browser or driver version error | Chrome and ChromeDriver major versions do not match, or the image version is unexpected | Pin and inspect the full Selenium image tag; confirm browser and driver major versions match. |
| Headless startup fails after an Xvfb change | The required Xvfb behavior may depend on Chrome and docker-selenium versions | Read the README for the precise image tag and apply its SE_START_XVFB guidance. |
| The screenshot is absent from CI artifacts | The test process writes the file in a different filesystem or directory from the artifact collector’s target | Identify where the Python process runs, verify the path is writable, and collect the artifact from that process-side path. |
| The image has unexpected size or framing | The viewport was not set, or a viewport screenshot was expected to include the full page | Set the window size explicitly. Confirm whether you need the current viewport, an element capture, or another full-page approach. |
| The screenshot is taken before the page looks ready | The capture runs before the relevant content appears | Wait for a page condition or target element in your Selenium flow before saving. Choose a condition tied to the content you need rather than relying on an arbitrary delay alone. |
5. Performance, reliability, and cost notes
- Pin the environment: use a full image tag and keep the browser image consistent across local and CI runs. This reduces surprise changes when an unpinned image updates.
- Right-size shared memory: browser stability can depend on the page and parallel workload. The documented 2 GB setting is a starting point to tune.
- Keep captures deterministic: specify viewport dimensions and wait for the page state relevant to the test. Dynamic content, fonts, and late-loading elements can change what appears in the result.
- Plan artifact storage: remote WebDriver returns screenshot bytes through the session, while the binding writes the requested file path. Make output directories and CI artifact paths explicit.
- Account for browser operations: this approach requires a running browser container and WebDriver session. The browser setup, image execution, and CI resources are part of the operating cost; the cited Selenium guidance does not provide a universal cost or performance benchmark.
Or skip the browser setup
For a one-call capture without managing Chrome and Selenium, use ScreenshotNeo. The API can return a screenshot or PDF, and its documentation describes the request options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots; and 1,000 screenshots a month are free with no card, with paid plans starting at $5 for 3,000. Sign up for 1,000 free screenshots a month, no card required.
FAQ
Does a Selenium screenshot save inside the browser container?
With remote WebDriver, the binding receives the screenshot and writes it to the path you pass. In the example, that is the Python process’s filesystem.
Can I capture only one element?
Yes. Selenium supports an element-level screenshot method; locate the element and save its screenshot when it is present and visible.
Is --headless=new enough for every Docker image?
Not necessarily. Headless and Xvfb requirements vary by browser and image version. Follow the configuration guidance for the exact docker-selenium tag in use.


