ScreenshotNeo

BlogHow-to

Capture Website Screenshots with Java Selenium in Docker

Capture a website screenshot with Java Selenium in Docker using a repeatable browser setup, explicit viewport, and reliable file handling.

By the ScreenshotNeo team4 October 20269 min read

To capture a website screenshot with Java Selenium in Docker, run a Selenium browser container or Grid, connect to its WebDriver endpoint with RemoteWebDriver, navigate to the URL, set a consistent browser window size, and save the bytes returned by TakesScreenshot. Docker determines where the browser runs; the Java code still issues ordinary WebDriver commands. Selenium’s basic screenshot API captures the current browsing context, so it does not by itself guarantee a full-page image. Selenium’s window and screenshot documentation describes that capture flow.

1. Choose where Selenium runs the browser

For a one-machine task, a local WebDriver session can be simplest. For a browser running in Docker, the Java process connects remotely to Selenium Server or Grid. Grid routes WebDriver commands to remote browser instances, and Selenium documents it for parallel runs and testing across browser versions and platforms. The simplest remote setup is a standalone Grid; when Java and Grid share a host, its default endpoint is http://localhost:4444. If Java runs in another container, use the Grid service name and internal port on a shared Docker network, not localhost (which means the Java container itself).

Setup Good fit Considerations
Local browser and driver Single-machine scripts and quick experiments Browser installation and version are tied to the machine running Java.
Dockerized standalone Grid Repeatable browser environment or an isolated capture worker Requires a reachable Grid endpoint and matching browser capability.
Multi-node Grid Parallel work or multiple browser versions/platforms Requires node availability, capacity planning, and protected network access.

Selenium’s Grid guide lists Java 11 or higher for its getting-started instructions and points to Docker as a useful deployment approach. Check the Selenium Downloads page and the official Docker Selenium repository for current releases and matching image tags. Pin a versioned image for repeatability; a moving latest tag can change the browser environment over time.

2. Start a Selenium browser container

Run the official standalone Chrome image on a trusted machine or network. This example maps the Grid port on the host so a Java process running on that same host can connect:

docker run -d --name selenium-chrome \
  -p 127.0.0.1:4444:4444 \
  selenium/standalone-chrome:latest

The tag is illustrative. For repeatable runs, replace latest with a versioned tag available in the Docker Selenium repository and keep it aligned with the Selenium client version you use. This port binding limits host access to loopback; for Java in another container, create a private Docker network and connect via the Grid service name instead. Do not expose an unauthenticated Grid publicly. Selenium warns that an exposed Grid can provide access to its infrastructure, internal applications and files, and may let third parties run custom binaries. See the Grid getting-started guide for setup and security guidance.

3. Create a Java project

This example uses Maven and Java 11 or newer. Use the current stable Selenium Java version from the official downloads page; the version below is a placeholder, not a fixed recommendation.

<dependencies>
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>REPLACE_WITH_CURRENT_SELENIUM_VERSION</version>
  </dependency>
</dependencies>

Save the following as Screenshot.java. It accepts an optional URL and output path, defaults to https://example.com and shot.png, waits for the document ready state, sets a desktop window size, captures the current viewport, and always quits the session. Set GRID_URL to override the default endpoint.

import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;

import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;

public class Screenshot {
  public static void main(String[] args) throws Exception {
    String target = args.length > 0 ? args[0] : "https://example.com";
    Path output = Path.of(args.length > 1 ? args[1] : "shot.png");
    String endpoint = System.getenv().getOrDefault("GRID_URL", "http://localhost:4444");

    ChromeOptions options = new ChromeOptions();
    // Add browser arguments here only when the container or workload requires them.

    WebDriver driver = new RemoteWebDriver(new URL(endpoint), options);
    try {
      driver.manage().window().setSize(new Dimension(1440, 1000));
      driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(60));
      driver.get(target);

      // Navigation completion does not mean every dynamic component has rendered.
      new WebDriverWait(driver, Duration.ofSeconds(15)).until(
          d -> "complete".equals(
              ((org.openqa.selenium.JavascriptExecutor) d)
                  .executeScript("return document.readyState")));
      // Replace this with a selector for content that matters on your target site.
      new WebDriverWait(driver, Duration.ofSeconds(15)).until(
          ExpectedConditions.presenceOfElementLocated(By.tagName("body")));

      byte[] png = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
      Path parent = output.toAbsolutePath().getParent();
      if (parent != null) Files.createDirectories(parent);
      Files.write(output, png);
      System.out.println("Saved screenshot to " + output.toAbsolutePath());
    } finally {
      driver.quit();
    }
  }
}

Compile and run with Maven’s exec plugin or from your IDE after resolving the dependency. For example, once the class is compiled and dependencies are on the classpath:

GRID_URL=http://localhost:4444 java -cp "target/classes:target/dependency/*" Screenshot \
  https://example.com artifacts/example.png

The exact packaging command depends on your Maven project configuration. You can also run Java itself in a container, provided that container can reach Grid over a private Docker network. Selenium Manager can configure drivers for supported local sessions when enabled; with a remote session, the browser and its driver are managed by the Grid/browser environment, not by a driver installed on the Java client host.

4. Save the screenshot from a Dockerized Java client

When both Java and Grid run in containers, put them on the same user-defined Docker network. The Java program’s GRID_URL should use the Grid container or service DNS name, such as http://selenium-chrome:4444. Mount an output directory into the Java container if the resulting file must be available on the host. A file written inside an unmounted container filesystem will not automatically appear on the host.

The returned screenshot bytes are transferred to the Java client, then written to its filesystem. Make sure the output directory exists and is writable. Use an explicit path so automation jobs do not silently save into an unexpected working directory.

5. Control viewport and page readiness

Window size affects responsive layout and therefore the output. Specify it for every run if you compare images. The example requests a 1440 by 1000 browser window, but window dimensions and the page’s CSS viewport are not necessarily interchangeable across environments; choose the dimensions you need and verify the rendered result in your target container.

Navigation returning does not prove that an application finished fetching data, loading lazy content, or animating. Wait for a meaningful selector or application-specific readiness condition. Prefer a condition tied to the element that must appear over an arbitrary long sleep. A fixed delay can still be appropriate for a known animation or third-party widget, but increases runtime and does not guarantee readiness.

6. Viewport screenshots versus full-page images

The basic TakesScreenshot call shown above captures the current browsing context and must not be described as a guaranteed full-page capture. If the entire page is required, validate a browser-specific full-page technique for your exact Chrome version and Grid image, or scroll through the document and stitch viewport images. Scrolling can trigger lazy-loaded images and alter sticky elements; stitching can duplicate or miss content when the page changes during capture. For reliable output, test long pages, fixed headers, lazy images, and pages with infinite scrolling explicitly.

7. Other useful capture options

  • Browser choice: use options for the browser supported by the Grid node and image. A requested browser without an available matching node cannot start a session.
  • Window size: set a consistent size before navigation or before capture, and record it with the output.
  • Page-load timeout: set a finite timeout so a stalled navigation does not hang a worker indefinitely.
  • Explicit waits: wait for target content or a site-specific ready signal when pages are dynamic.
  • Output type: Selenium can return screenshot data as bytes or a temporary file; bytes are straightforward to write to a chosen path.
  • Session cleanup: call quit() in a finally block so the remote session is released after success or failure.

8. Troubleshooting

Symptom Likely cause Fix
Connection refused at port 4444 Grid is stopped, the port is not published, or the Java process is using the wrong host. Check the container is running and the port mapping. From another container, use its Grid network name rather than localhost.
Session cannot be created No node matches the requested browser, image capabilities differ, or client and server versions are incompatible. Check the Grid console/logs, start the matching browser image, and align Selenium versions and capabilities.
Screenshot is blank or content is missing The page is still rendering, a navigation failed, content loads asynchronously, or the page blocks the browser. Check the URL and browser logs, wait for a meaningful page element, and inspect the target from the same browser environment.
Screenshot has the wrong layout Viewport/window size differs or responsive CSS selected another breakpoint. Set a deterministic window size and check the actual viewport in the container.
Output file is missing The path is relative to an unexpected working directory, parent directory is absent, or a container volume is not mounted. Use an absolute path, create parent directories, and mount the output directory when running Java in Docker.
Navigation times out The site is slow, unreachable from the browser container, or never reaches the selected page-load condition. Check network/DNS access from the browser container, set a suitable finite page-load timeout, and wait for the specific content needed.
Grid is reachable from the public internet Port binding or firewall rules expose an unauthenticated endpoint. Bind to loopback or a trusted private network and apply firewall controls. Do not publish Grid casually.
Screenshot is only one viewport tall The basic screenshot method captures the current browsing context. Use and validate a separate full-page strategy; do not assume Docker changes the screenshot semantics.

9. Performance, reliability, and cost

A capture consumes time and resources for browser startup, navigation, readiness waits, and transferring image bytes. Reuse a session for multiple pages when appropriate, but isolate sessions when pages need different state or failures must not leak between jobs. Close every session, set finite timeouts, and bound Grid concurrency to the capacity of its browser nodes. For stable comparisons, keep the browser image, Java/Selenium versions, viewport, target URL, readiness condition, and relevant network conditions consistent.

Docker itself does not make a capture reliable or free of variability: image updates, fonts, network responses, animations, and site changes can alter output. Pin the browser image, keep dependencies controlled, and capture diagnostic details such as the requested URL, viewport, and exception when a job fails. Selenium and browser containers have infrastructure costs in CPU, memory, storage, and operations; the dossier provides no universal cost or performance benchmark, so measure against your workload.

Or skip the browser setup

ScreenshotNeo accepts one GET request with a URL and returns a clean PNG, JPEG, WebP, or PDF. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed; response headers identify the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. See the ScreenshotNeo API documentation for parameters and configuration.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp
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)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. It supports full-page capture, element capture, device and viewport choices, custom CSS and JavaScript, wait conditions, caching, async jobs, bulk capture, and other options. Create a free account for 1,000 screenshots a month with no card.

FAQ

Does Selenium need ChromeDriver installed on the Java host?

Not for a remote browser session in Docker Grid. The browser-side environment supplies and manages the browser and driver; the Java client connects to the Grid endpoint.

Can I save the screenshot directly from the browser container?

The standard WebDriver screenshot result is returned to the client. Write it from Java to the client filesystem, or mount a shared output volume if Java runs in a container and the host needs the file.

Does this method create a PDF?

No. This example saves a PNG screenshot. PDF output requires a separate browser or API workflow and should be configured and validated for the page and output requirements.

Where should I check Selenium version changes?

Use Selenium’s official Downloads page and verify the Docker Selenium repository’s available tags before updating the client and browser image.