Selenwright is a Docker-native browser automation grid built for first-class Playwright WebSocket sessions, with Selenium WebDriver compatibility when you need it. It starts an isolated browser container for every session, exposes Playwright through a native /playwright/<browser>/<version> WebSocket endpoint, and gives teams the operational pieces missing from raw browser containers: queueing, VNC, video, logs, artifacts, authentication, browser discovery, and a web UI. Please refer to GitHub repository if you need source code.

1. Why Selenwright

  • Native Playwright in Docker - connect Playwright clients directly over WebSocket without Selenium compatibility mode.

  • One grid for Playwright and Selenium - run modern Playwright suites and existing WebDriver tests from the same service.

  • Ephemeral browser containers - every session gets an isolated Docker browser with predictable cleanup.

  • Debuggable CI browser automation - capture VNC, video, logs, downloads, metadata, and session artifacts.

  • Operator-friendly by default - use authentication, quotas, browser discovery, metrics, and Selenwright UI in production-style environments.

2. Getting Started

2.1. Quick Start Guide

2.1.1. Start Selenwright

  1. Make sure you have recent Docker version installed.

  2. Download Selenwright binary for your platform from releases page.

  3. On Linux or Mac give execution permissions to binary:

    $ chmod +x selenwright
  4. Run Selenwright:

    *nix
    $ ./selenwright
    Windows
    selenwright.exe

    Running this command with sudo can lead to broken installation. Recommended way is running Selenwright as regular user. On Linux to have permissions to access Docker you may need to add your user to docker group:

    $ sudo usermod -aG docker $USER
  5. Optionally start Selenwright UI to see live browser sessions.

2.1.2. Connect Playwright

Connect Playwright to Selenwright’s native WebSocket endpoint:

Playwright WebSocket endpoint
ws://localhost:4444/playwright/chromium/1.56.1
Playwright example
const { chromium } = require("playwright");

const browser = await chromium.connect(
  "ws://localhost:4444/playwright/chromium/1.56.1",
  {
    headers: {
      Authorization: `Bearer ${process.env.SELENWRIGHT_TOKEN}`,
    },
  }
);

The Playwright client version and browser image version must match on major and minor version. For example, Playwright 1.56.x should connect to a Selenwright browser entry built for 1.56.x.

2.1.3. Connect Selenium WebDriver

Run existing Selenium tests against the WebDriver compatibility endpoint:

Selenium WebDriver endpoint
http://localhost:4444/wd/hub

2.1.4. Check Status and UI

If something does not work, you can check that Selenwright is running by opening the status URL:

Current Selenwright Status
http://localhost:4444/status

A successful request should return JSON with browser usage statistics.

To open Selenwright UI navigate to the following page in your browser:

UI Default URL
http://localhost:4173/

2.2. Starting Selenwright Manually

  1. This guide shows how to start Selenwright manually.

  2. This guide assumes you already know how to deal with command line and know basic Docker commands.

2.2.1. Prepare Browsers

There are two ways to populate the browser catalog:

Pull the browser images you need and let Selenwright discover them automatically:

$ docker pull selenwright/playwright-chromium:latest
$ docker pull selenwright/playwright-firefox:latest
$ docker pull selenwright/playwright-webkit:latest

Start Selenwright — it will detect the pulled images via Docker labels. Then open Selenwright UI and adopt the discovered images into the catalog. No configuration file needed.

Option B: Manual browsers.json

Create config/browsers.json configuration file:

config/browsers.json
{
    "chromium": {
        "default": "1.56.1",
        "versions": {
            "1.56.1": {
                "image": "selenwright/playwright-chromium:latest",
                "port": "3000",
                "path": "/",
                "protocol": "playwright"
            }
        }
    },
    "firefox": {
        "default": "1.56.1",
        "versions": {
            "1.56.1": {
                "image": "selenwright/playwright-firefox:latest",
                "port": "3000",
                "path": "/",
                "protocol": "playwright"
            }
        }
    }
}
Playwright images expose port 3000 at path / and require "protocol": "playwright". For classic Selenium images use "path": "/" for Chrome/Opera and "path": "/wd/hub" for Firefox. See Browsers Configuration File for all fields.

Pull the images:

$ docker pull selenwright/playwright-chromium:latest
$ docker pull selenwright/playwright-firefox:latest

Selenwright falls back to this file when no adopted images are found.

Browser Images

Selenwright publishes Playwright-based browser images on Docker Hub:

Each image carries io.selenwright.* Docker labels so Browser Discovery picks them up without a configuration file. Dockerfiles live in aqa-alex/selenwright-browsers.

Classic Selenium-protocol images are not published under selenwright/ — bring your own (for example, upstream selenoid/chrome, selenoid/firefox).

Browser Discovery and browsers.json are mutually exclusive: as soon as Selenwright finds any adopted image, it builds the catalog from Docker labels and ignores browsers.json entirely. To mix Playwright and classic Selenium browsers, pick one mechanism and put all entries there:

  • JSON-only — don’t adopt anything in the UI; list every browser (Playwright and Selenium) in browsers.json.

  • Discovery-only — tag your Selenium images with the required io.selenwright.* labels (see selenwright-browsers for the label contract) and adopt them alongside the Playwright images.

2.2.2. Start Selenwright

Option 1: start Selenwright binary
  1. Download binary for your operating system from releases page and save it as selenwright (or selenwright.exe on windows).

    Add execution permission in case of *nix os-type with chmod +x selenwright.
  2. Then run:

    *nix
    ./selenwright
    Windows
    selenwright.exe
  3. It should write to console something like:

    2017/11/26 21:23:43 Loading configuration files...
    2017/11/26 21:23:43 Loaded configuration from [config/browsers.json]
    
    ...
    
    2017/11/26 21:23:43 Listening on :4444
Option 2: start Selenwright container

If you have Docker installed you can omit downloading binary and run it inside container. Pull browser images, then run:

*nix
docker run -d                                   \
--name selenwright                                 \
-p 4444:4444                                    \
-v /var/run/docker.sock:/var/run/docker.sock    \
selenwright/hub:latest-release
If you use a manual browsers.json (Option B above), mount it as a volume: -v /your/directory/config/:/etc/selenwright/:ro and add -conf /etc/selenwright/browsers.json to the command.

2.3. Frequently Asked Questions

2.3.1. Logs and Dirs

Where are Selenwright logs?

Selenwright outputs its logs to stdout. Selenwright launched as a binary should output logs to the screen. To see Selenwright logs launched as Docker container type:

$ docker logs selenwright

To follow the logs add one more flag:

$ docker logs -f selenwright

Where are recorded videos stored?

Videos are saved to the directory specified by -video-output-dir flag (default video relative to the working directory). When running in Docker, mount a host directory to this path — see Video Recording for details.

2.3.2. Limits and Timeouts

How can I limit overall browsers consumption?

You have to use -limit flag to specify total number of parallel sessions. Default value is 5. See Resources Consumption section on how to determine total number of parallel sessions.

Can I limit per-version browser consumption?

No, this is not supported. We consider the only reasonable limitation should be the overall browsers consumption. This is important to not overload the hardware.

How can I adjust Selenwright timeouts?

The main timeout flag is -timeout, specified as 60s or 2m or 1h. It means maximum amount of time between subsequent HTTP requests to Selenium API. When there are no requests during this time period - session is automatically closed. Selenwright also has more subtle timeouts like:

  • -service-startup-timeout - container or driver process startup timeout

  • -session-attempt-timeout - new session HTTP request timeout, applied when container or driver has started

  • -session-delete-timeout - container or process removal timeout, applied after driver.quit() call

2.3.3. Resources Consumption

How many resources browser containers consume?

This depends on your tests. We recommend to start with 1 CPU and 1 Gb of memory per container as a rough estimate and then increase -limit checking that your tests work stably.

Do VNC and non-VNC browser images memory and CPU consumption differ?

The only difference between these images - is a running VNC server (x11vnc) consuming approximately 20 Megabytes of RAM in idle state which is negligible compared to browser memory consumption.

2.3.4. Features not Working

Video feature not working

When running Selenwright as Docker container video feature can be not working (because of misconfiguration). If your video files are named like selenwright607667f7e1c7923779e35506b040300d.mp4 and you are seeing the following log message…​

2018/03/20 21:06:37 [9] [VIDEO_ERROR] [Failed to rename /video/selenwright607667f7e1c7923779e35506b040300d.mp4 to /video/8019c4bc-9bec-4a8b-aa40-68d1db0cffd2.mp4: rename /video/selenwright607667f7e1c7923779e35506b040300d.mp4 /video/8019c4bc-9bec-4a8b-aa40-68d1db0cffd2.mp4: no such file or directory]

... then check that:

  1. You are passing an OVERRIDE_VIDEO_OUTPUT_DIR environment variable pointing to a directory on the host machine where video files are actually stored

  2. When passing custom arguments to Selenwright container (such as -limit or -timeout) you also have to pass -video-output-dir /opt/selenwright/video and mount host machine video dir to /opt/selenwright/video

Can’t get VNC feature to work: Disconnected

Please check that you have enableVNC = true capability in your tests

Can Selenwright pull browser images automatically?

Selenwright does not auto-pull images during session creation to avoid unpredictable latency under load. However, you can use the Browser Discovery feature to scan images already present on the host and adopt them into the catalog. For stack-wide updates, see Docker Compose Stack Management.

3. Main Features

3.1. Native Playwright Support

Selenwright can proxy native Playwright WebSocket connections through a dedicated endpoint. This support is for direct Playwright clients and companion Playwright server images. It is not Selenium Grid compatibility mode.

3.1.1. What Selenwright Supports

  • One Playwright WebSocket connection maps to one Selenwright-managed browser session.

  • Selenwright starts the configured browser container, connects the client to the Playwright server running inside it, and tracks the session in the same queue and /status model as existing sessions.

  • Idle timeout and disconnect cleanup stop the container and remove the session from Selenwright state.

3.1.2. Endpoint Format

Use the following endpoint:

ws://<host>:4444/playwright/<browser>/<playwright-version>

Where:

  • <browser> matches a top-level browser name in the browser catalog (e.g. chromium, firefox, webkit).

  • <playwright-version> matches the configured browser version entry.

This route accepts WebSocket connections only.

3.1.3. Session Lifecycle And Cleanup

When a client connects to /playwright/<browser>/<playwright-version>, Selenwright resolves the matching browser entry, starts the container, and registers a session after the WebSocket tunnel is established.

Playwright sessions follow the same operational model as WebDriver sessions:

  • Queue admission and release behave the same way as for regular Selenwright sessions.

  • Active sessions are visible in /status.

  • If the client disconnects, the upstream Playwright server disconnects, or the idle timeout expires, Selenwright stops the container and removes the session.

3.1.4. Companion Image Contract

Companion Playwright images are external to this repository. To work with Selenwright they should follow this contract:

  • Pin the image tag to a Playwright version.

  • Run a Playwright server, not Selenium.

  • Expose one fixed port and declare that same port in the browser catalog.

  • Expose one fixed WebSocket path. The default path is /.

  • Run under --init or an equivalent init process so child processes are reaped correctly.

  • Define Docker HEALTHCHECK.

  • For Browser Discovery pickup, carry the io.selenwright.* labels described below. Without them the image is invisible to discovery and must be registered via browsers.json.

Table 1. Required Docker labels for Browser Discovery
Label Purpose

io.selenwright.browser

Browser name (chromium, firefox, webkit, …​). Required.

io.selenwright.version

Version entry used in the catalog and connection URL. Required.

io.selenwright.port

Port exposed by the image. Required unless io.selenwright.config is used.

io.selenwright.protocol

Set to playwright for Playwright images.

io.selenwright.path

WebSocket path (default /).

io.selenwright.default

true to mark this version as default for the browser.

Optional labels mirror the per-field names in browsers.json: tmpfs, env, volumes, hosts, shmSize, mem, cpu, labels, sysctl. As an escape hatch, io.selenwright.config accepts a full Browser JSON blob; when present, per-field labels are ignored. See selenwright-browsers for a working example.

At startup Selenwright prefers Docker HEALTHCHECK when the image provides one. If health status is unavailable, Selenwright falls back to protocol-specific probing. For Playwright images that fallback is a TCP probe against the configured Playwright endpoint.

3.1.5. Version Matching Rule

Playwright client and server versions must match on major and minor version.

For example, a client from Playwright 1.56.x should connect to an image built for Playwright 1.56.x. Keep the image tag, the configured Selenwright version entry, and the client connection URL aligned.

3.1.6. Configuration Example

An example browser catalog entry for a native Playwright image (via browsers.json or Browser Discovery):

{
  "chromium": {
    "default": "1.56.1",
    "versions": {
      "1.56.1": {
        "image": "selenwright/playwright-chromium:latest",
        "port": "3000",
        "path": "/",
        "protocol": "playwright"
      }
    }
  }
}

Selenwright publishes matching companion images — see Browser Images for the full list.

3.1.7. Usage Examples

Connect from Playwright with the dedicated Selenwright WebSocket endpoint:

const browser = await browserType.connect(
  "ws://selenwright.example.com:4444/playwright/chromium/1.56.1"
);
Passing an API Token

When selenwright has authentication enabled (the default), pass the bearer token in the Authorization header:

const browser = await browserType.connect({
  wsEndpoint: "wss://selenwright.example.com/playwright/chromium/1.56.1",
  headers: { Authorization: `Bearer ${process.env.SELENWRIGHT_TOKEN}` },
});

For clients that cannot set custom WebSocket headers, selenwright also accepts a ?token=<plaintext> query fallback:

const browser = await browserType.connect(
  `wss://selenwright.example.com/playwright/chromium/1.56.1?token=${process.env.SELENWRIGHT_TOKEN}`
);
The query form puts the token in URL-shaped surfaces — shell history, CI logs, the browser’s reverse-proxy access log — even though selenwright strips the parameter before forwarding the request upstream. Prefer the header form whenever your client supports it.

See API Tokens (for Machine Clients) in the Authentication chapter for how tokens are minted and revoked.

If your test bootstrap reads an environment variable to choose the remote endpoint, you can pass the same URL through it:

PW_TEST_CONNECT_WS_ENDPOINT=ws://selenwright.example.com:4444/playwright/chromium/1.56.1

PW_TEST_CONNECT_WS_ENDPOINT is only a user-side convention for your own Playwright config or test launcher. It is not a built-in Selenwright environment variable.

3.1.8. File Operations

File uploads (page.setInputFiles()) and downloads (page.download()) work natively through the Playwright WebSocket protocol. Selenwright proxies the connection transparently — no additional configuration is needed.

The Selenwright-specific /file and /download/ HTTP endpoints are WebDriver-only and are not used by Playwright sessions. Use native Playwright APIs for file operations.

3.1.9. Capabilities

A subset of Special Capabilities can be passed as query parameters on the WebSocket URL:

ws://host:4444/playwright/chromium/1.56.1?enableVNC=true&name=myTest&screenResolution=1280x1024

Supported: enableVNC, name, screenResolution.

Session logs (Playwright protocol messages) are captured automatically when artifact history is enabled.

3.1.10. External Session IDs (Router Integration)

Selenwright honors X-Selenwright-External-Session-ID on the Playwright upgrade request. When the header is present and valid, its value replaces the auto-generated session ID and is used as-is for:

  • the app.sessions key,

  • the path segment in side endpoints (/vnc/<id>, /video/<id>.mp4, /logs/<id>, /devtools/<id>, /clipboard/<id>),

  • log file names and artifact history entries.

The header is intended for routers (e.g., gridlane) that need to encode routing metadata into the public session ID before the upgrade reaches selenwright — otherwise the router’s advertised ID would not match the one selenwright stored, and side endpoints would 404.

The value must match [a-zA-Z0-9_-]{1,128}; invalid values and collisions with existing sessions are rejected with 400 Bad Request before any browser container is started.

If the header is absent or blank, selenwright falls back to an internally generated 32-hex ID (the original behavior); standalone Playwright clients are unaffected.

3.2. Video Recording

  1. This feature only works when browsers are run in containers.

  2. Video can be recorded with both vnc or non-vnc browser images.

  3. An additional enableVideo capability should be added to tests. See Special Capabilities section for more details.

Selenwright can capture browser screen and save it to MPEG-4 video file using H264 codec. Video recording works by attaching a separate container with video capturing software to running browser container.

To use this feature you should:

  1. Pull video recorder image once:

    $ docker pull selenwright-video-recorder:latest-release
  2. When running Selenwright in Docker container:

    1. Mount a host directory to /opt/selenwright/video/ to persist recordings.

    2. Pass OVERRIDE_VIDEO_OUTPUT_DIR with the host-side absolute path to the same directory. The video recorder runs in a separate container and needs the host path to write files correctly.

  3. When running Selenwright as a binary — videos are stored in the -video-output-dir directory (default video) relative to the working directory.

    Example Docker Command
    $ docker run -d                                 \
    --name selenwright                                 \
    -p 4444:4444                                    \
    -v /var/run/docker.sock:/var/run/docker.sock    \
    -v /data/video/:/opt/selenwright/video/            \
    -e OVERRIDE_VIDEO_OUTPUT_DIR=/data/video/       \
    selenwright/hub:latest-release

3.2.1. Downloading Video Files from Selenwright

You can access recorded video files using the following URL:

Direct Link to File
http://selenwright-host.example.com:4444/video/<filename>.mp4
Direct link will work only after session has finished because Selenwright renames temporary filename to <session-id>.mp4 (by default) at the session close.

To see all available files use:

Listing All Video Files
http://selenwright-host.example.com:4444/video/

3.2.2. Deleting Video Files

When Artifact History is enabled, video files are automatically cleaned up by the retention janitor. Otherwise, to limit storage space consumption you have two alternatives:

  1. Schedule scripts automatically removing files older than desired date and time. An example command under Unix operating systems can look like:

    Shell Command to Remove Old Video Files
    $ find /path/to/video/dir -mindepth 1 -maxdepth 1 -mmin +120 -name '*.mp4' | xargs rm -rf

    Notice -mmin +120 argument meaning to only process files older than 2 hours (120 minutes).

  2. Send video removal requests (e.g. from passed test cases). Just use DELETE HTTP method and Selenwright video URL:

    Deleting Video File via HTTP API
    $ curl -X DELETE http://selenwright-host.example.com:4444/video/<filename>.mp4

3.3. Saving Session Logs

An additional enableLog capability should be added to tests. See Special Capabilities section for more details.

Selenwright can save log files for every running session to a separate file. By default log files are saved as <session-id>.log but you can alter file name via logName capability.

To enable this feature you only need to add -log-output-dir </path/to/some/dir> flag to Selenwright:

$ ./selenwright -log-output-dir /path/to/some/dir

When running Selenwright in Docker container - don’t forget to mount logs directory from the host machine:

Example Docker Command
$ docker run -d                                 \
--name selenwright                                 \
-p 4444:4444                                    \
-v /var/run/docker.sock:/var/run/docker.sock    \
-v /data/logs/:/opt/selenwright/logs/              \
selenwright/hub:latest-release -log-output-dir /opt/selenwright/logs

3.3.1. Downloading Log Files from Selenwright

You can access saved log files using the following URL:

Direct Link to Log File
http://selenwright-host.example.com:4444/logs/<filename>.log
Direct link will work only after session has finished because Selenwright renames temporary filename to <session-id>.log (by default) at the session close.

To see all available files use:

Listing All Log Files
http://selenwright-host.example.com:4444/logs/

3.3.2. Deleting Log Files

When Artifact History is enabled, log files are automatically cleaned up by the retention janitor. Otherwise, either remove log files from the directory using a scheduled job or send log removal requests:

Deleting Log File via HTTP API
$ curl -X DELETE http://selenwright-host.example.com:4444/logs/<filename>.log

3.4. Uploading Files To Browser

Some tests require to upload files. This feature works out of the box in the majority of Selenium clients. A typical Java code snippet look like the following:

Uploading files with Java
// Find file input element
WebElement input = driver.findElement(By.cssSelector("input[type='file']"));

// Make sure element is visible
((JavascriptExecutor) driver).executeScript("arguments[0].style.display = 'block';", input);

// Configure your client to upload local files to remote Selenium instance
driver.setFileDetector(new LocalFileDetector());

// Specify you local file path here (not path inside browser container!)
input.sendKeys("/path/to/file/on/machine/which/runs/tests");
Uploading files with Python
from selenium.webdriver.remote.file_detector import LocalFileDetector

# ...

input = driver.find_element_by_css_selector("input[type='file']")
driver.execute_script("arguments[0].style.display = 'block';", input)
driver.file_detector = LocalFileDetector()
input.send_keys("/path/to/file/on/machine/which/runs/tests")
Uploading files with Webdriver.io
var filePath = path.join('/path/to/file/on/machine/which/runs/tests');
var remoteFilePath = browser.uploadFile(filePath);
$("input[type='file']").setValue(remoteFilePath);

Read the following note if you are using Selenwright without Docker.

This feature is supported in WebDriver protocol by sending zipped file contents to /file handle. However not all driver binaries support this feature (e.g. Geckodriver). When proxying requests directly to drivers (i.e. when not using Docker) you need to start Selenwright with -enable-file-upload flag. In that case Selenwright will provide the required API to tests. Firefox container images already include this parameter where needed.

3.4.1. HTTP Endpoint

When Selenwright is started with -enable-file-upload, an additional route is registered:

Method, path

POST /file

Content-Type

application/json

Request body

{"file": "<base64-encoded zip archive>"}

Response

JSON {"value": "<absolute path to the extracted file on the Selenwright host>"} — the returned path is what the test client then passes to sendKeys() on the file input.

Auth

Same middleware as /wd/hub/* (secret auth, htpasswd, etc. — whatever -users/-auth is configured with).

The zip archive must contain exactly one file. Selenwright extracts it into a per-session temp directory and returns that path.

If the flag is not set the route is not registered and the server replies 404 Not Found. Selenium clients that use LocalFileDetector will transparently fall back to the driver-level upload when that happens — you usually do not need to handle 404 yourself.

Table 2. Request body size limits

-max-upload-body-bytes

POST body cap, default 256 MiB. Affects the base64-wrapped payload size over the wire.

-max-upload-extracted-bytes

Cap on the uncompressed size of the archive contents, default 1 GiB. Protects against zip-bomb style payloads.

Both limits reject over-sized requests with 400 Invalid Argument and close the connection — the zip is never written to disk past the limit.

3.5. Downloading Files From Browser

The RemoteWebDriver examples below assume anonymous access for readability. When selenwright has authentication enabled (the default), supply an API token via ClientConfig.addHeader("Authorization", "Bearer " + token) in Java or the equivalent in your WebDriver binding, instead of embedding user:pass@ in the URL. See API Tokens (for Machine Clients).

3.5.1. Downloading Files in Different Browsers

Chrome
Downloading files with Java
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setExperimentalOption("prefs", new HashMap<String, Object>(){
    {
        put("profile.default_content_settings.popups", 0);
        put("download.default_directory", "/home/selenium/Downloads");
        put("download.prompt_for_download", false);
        put("download.directory_upgrade", true);
        put("safebrowsing.enabled", false);
        put("plugins.always_open_pdf_externally", true);
        put("plugins.plugins_disabled", new ArrayList<String>(){
            {
                add("Chrome PDF Viewer");
            }
        });
    }
});

WebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), chromeOptions);
driver.navigate().to("http://example.com/myfile.odt");
Firefox
Downloading files with Java
FirefoxOptions firefoxOptions = new FirefoxOptions();
firefoxOptions.setCapability("moz:firefoxOptions", new HashMap<String, Object>(){
    {
        put("prefs", new HashMap<String, Object>(){
            {
                put("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream");
            }
        });
    }
});


WebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), firefoxOptions);
driver.navigate().to("http://example.com/myfile.odt");

3.5.2. Accessing Files Downloaded with Browser

  1. This feature only works when browsers are being run in containers.

  2. Files are accessible only when browser session is running.

Your tests may need to download files with browsers. To analyze these files a common requirement is then to somehow extract downloaded files from browser containers. A possible solution can be dealing with container volumes. But Selenwright provides a /download API and dramatically simplifies downloading files. Having a running session f2bcd32b-d932-4cdc-a639-687ab8e4f840 you can access all downloaded files using an HTTP request:

GET http://selenwright-host.example.com:4444/download/f2bcd32b-d932-4cdc-a639-687ab8e4f840/myfile.txt

In order for this feature to work an HTTP file server should be listening inside browser container on port 8080. Download directory inside container to be used in tests is usually ~/Downloads.

Similarly to delete downloaded files replace GET request with DELETE:

DELETE http://selenwright-host.example.com:4444/download/f2bcd32b-d932-4cdc-a639-687ab8e4f840/myfile.txt

3.6. Accessing Clipboard

Clipboard is accessible only when browser session is running.

Sometimes you may need to interact with the clipboard to check that your application copy-paste feature works. Selenwright has a dedicated API to interact with the clipboard. To use it:

  1. Start a new session, for example with ID f2bcd32b-d932-4cdc-a639-687ab8e4f840.

  2. To get clipboard value send the following HTTP request:

    $ curl http://selenwright-host.example.com:4444/clipboard/f2bcd32b-d932-4cdc-a639-687ab8e4f840
    
    some-clipboard-value
  3. To update clipboard value:

    $ curl -X POST --data 'some-clipboard-value' http://selenwright-host.example.com:4444/clipboard/f2bcd32b-d932-4cdc-a639-687ab8e4f840

3.7. Accessing Browser Developer Tools

  1. Only available for classic Selenium Chrome images that expose a CDP relay on container port 7070 (for example upstream selenoid/chrome). The published selenwright/playwright-* images do not expose this port — Playwright clients access CDP through the native Playwright endpoint (see Native Playwright Support) instead.

  2. Only Chrome Developer Tools are supported.

  3. We recommend to use the most recent Chrome version possible.

Selenwright is proxying Chrome Developer Tools API to browser container. For every running Selenium session to access the API just use the following URL:

<ws or http>://selenwright.example.com:4444/devtools/<session-id>/<method>

Here <method> is one of the following:

Table 3. Supported Developer Tools Methods

Method

Protocol

Meaning

/browser

WebSocket

Developer Tools browser websocket

/

WebSocket

An alias for /browser

/page

WebSocket

Developer Tools current page (target) websocket - mainly useful when only one browser tab is open

/page/<target-id>

WebSocket

Developer Tools specified page (target) websocket - allows to connect to concrete browser tab

/json/protocol

HTTP

A list of supported Developer Tools protocol methods in JSON format (some client libraries are using it)

For example an URL to connect to current page websocket would be:

ws://selenwright.example.com:4444/devtools/<session-id>/page

3.8. Special Capabilities

Selenwright can improve some automation aspects with custom capabilities. You can pass it in tests to enable/disable some features.

3.8.1. Live Browser Screen: enableVNC

Selenwright supports showing browser screen during test execution. This works with containers having VNC server installed (VNC column of [Browser Image information]). To see browser screen:

Type: boolean
enableVNC: true

Then launch Selenwright UI to see the screen.

This works by proxying VNC port from started container to http://localhost:4444/vnc/<session-id>; to WebSocket, where <session-id> is Selenium session ID.

3.8.2. Custom Screen Resolution: screenResolution

Selenwright allows you to set custom screen resolution in containers being run:

Type: string, format: <width>x<height>x<colors-depth>
screenResolution: "1280x1024x24"
  • This capability sets only screen resolution - not browser window size. Most of browsers have some default window size value this is why your screenshot size can be smaller than screen resolution specified in capability. You should manually resize window to desired width and height.

  • So far as our containers run headless browsers in Xvfb without installed window manager maximize operation does not work. You need to use setSize method instead.

3.8.3. Video Recording: enableVideo, videoName, videoScreenSize, videoFrameRate, videoCodec

This feature requires some preparation. Please refer to Video Recording section for more details.

To enable video recording for session, add:

Type: boolean
enableVideo: true
  • By default saved video files are named <session-id>.mp4 where <session-id> is a unique identifier of Selenium session. To provide custom video name specify:

    Type: string
    videoName: "my-cool-video.mp4"
    It is important to add mp4 file extension.
  • By default the entire screen picture is being recorded. Specifying screenResolution capability changes recorded video size (width and height) accordingly. You can override video screen size by passing a capability. In case of videoScreenSize resolution is less than actual, screen on video will be trimmed starting from top-left corner:

    Type: string
    videoScreenSize: "1024x768"
  • Default video frame rate is 12 frames per second. Specifying videoFrameRate capability changes this value:

    Type: int
    videoFrameRate: 24
  • By default Selenwright is using libx264 codec for video output. If this codec is consuming too much CPU, you can change it using videoCodec capability:

    Type: string
    videoCodec: "mpeg4"

    To obtain the full list of supported values:

    $ docker run -it --rm --entrypoint /usr/bin/ffmpeg selenwright-video-recorder:latest-release -codecs

3.8.4. Saving Session Logs: enableLog, logName

This feature requires some preparation. Please refer to Saving Session Logs section for more details.

To enable saving logs for a session, add:

Type: boolean
enableLog: true

If you wish to automatically save logs for all sessions, you can enable this behavior globally with Selenwright -save-all-logs flag.

By default saved log files are named <session-id>.log where <session-id> is a unique identifier of Selenium session. To provide custom log file name specify:

Type: string
logName: "my-cool-log.log"
It is important to add log file extension.

3.8.5. Custom Test Name: name

For debugging purposes it is often useful to give a distinct name to every test case. When working with Selenwright you can set test case name by passing the following capability:

Type: string
name: "myCoolTestName"

The main application of this capability - is debugging tests in the UI which is showing specified name for every running session.

3.8.6. Custom Session Timeout: sessionTimeout

Sometimes you may want to change idle timeout for selected browser session. To achieve this - pass the following capability:

Type: string
sessionTimeout: 30m

Timeout is specified Golang duration format e.g. 30m or 10s or 1h5m and can be no more than the value set by -max-timeout flag.

3.8.7. Per-session Time Zone: timeZone

Some tests require particular time zone to be set in operating system. To achieve this with Selenwright use:

Type: string
timeZone: "Europe/Berlin"

You can find most of available time zones here. Without this capability launched browser containers will have the same timezone as Selenwright one.

3.8.8. Per-session Container Hostname: containerHostname

Override the hostname of the browser container:

Type: string
containerHostname: "my-browser-host"

3.8.9. Per-session Environment Variables: env

Sometimes you may want to set some environment variables for every test case (for example to test with different default locales). To achieve this pass one more capability:

Type: array, format: <key>=<value>
env: ["LANG=ru_RU.UTF-8", "LANGUAGE=ru:en", "LC_ALL=ru_RU.UTF-8"]

Environment variables from this capability are appended to variables from configuration file.

Under -caps-policy=strict (default), the env, dnsServers, hostsEntries, additionalNetworks, and applicationContainers capabilities are restricted to admin users. See Authentication and Authorization for details.

Sometimes you may need to link browser container to application container running on the same host machine. This allows you to use cool URLs like http://my-cool-app/ in tests. To achieve this simply pass information about one or more desired links via capability:

Type: array, format: <container-name>[:alias]
applicationContainers: ["spring-application-main:my-cool-app", "spring-application-gateway"]

3.8.11. Hosts Entries: hostsEntries

Although you can configure a separate list of /etc/hosts entries for every browser image in Browsers Configuration File sometimes you may need to add more entries for particular test cases. This can be easily achieved with:

Type: array, format: <hostname>:<ip-address>
hostsEntries: ["example.com:192.168.0.1", "test.com:192.168.0.2"]

Entries will be inserted to /etc/hosts before entries from browsers configuration file. Thus entries from capabilities override entries from configuration file if some hosts are equal.

3.8.12. Custom DNS Servers: dnsServers

By default Selenwright browser containers are using global DNS settings of Docker daemon. Sometimes you may need to override used DNS servers list for particular test cases. This can be easily achieved with:

Type: array, format: <dns-ip-address>
dnsServers: ["192.168.0.1", "192.168.0.2"]

3.8.13. More Docker Networks: additionalNetworks

By default Selenwright browser containers are started in Docker network specified by -container-network flag. If you tested application is running in another network you may need to connect browser container to this network:

Type: array, format: <network-name>
additionalNetworks: ["my-custom-net-1", "my-custom-net-2"]

3.8.14. Container Labels: labels

In big clusters you may want to pass additional metadata to every browser session: environment, VCS revision, build number and so on. These labels can be then used to enrich session logs and send them to a centralized log storage. Later this metadata can be used for more efficient search through logs.

Type: map, format: "<key>": "<value>"
labels: {"environment": "testing", "build-number": "14353"}

Labels from this capability override labels from browsers configuration file. When name capability is specified - it is automatically added as a label to container.

3.8.15. S3 Key Pattern: s3KeyPattern

This capability allows to override S3 key pattern (specified by -s3-key-pattern flag) used when uploading files to S3.

Type: string
s3KeyPattern: "$quota/$fileType$fileExtension"

The same key placeholders are supported. Please refer to Uploading Files To S3 section for more details.

3.8.16. Specifying Capabilities via Protocol Extensions

Some Selenium clients allow passing only a limited number of capabilities specified in WebDriver specification. For such cases Selenwright supports reading capabilities using WebDriver protocol extensions feature. The following two examples deliver the same result. Usually capabilities are passed like this:

Passing Capabilities as Usually
{"browserName": "firefox", "version": "57.0", "screenResolution": "1280x1024x24"}

Selenwright reads the selenoid:options key for protocol extension capabilities (kept for backward compatibility with existing test suites):

Passing Capabilities using Protocol Extensions
{"browserName": "firefox", "version": "57.0", "selenoid:options": {"screenResolution": "1280x1024x24"}}

4. Advanced Features

4.1. Usage Statistics

Selenwright calculates usage statistics that can be accessed with HTTP request:

Request
$ curl http://localhost:4444/status
Result
{
    "total": 80,
    "used": 10,
    "queued": 0,
    "pending": 1,
    "browsers": {
      "chromium": {
        "1.56.1": {
          "alice": {
            "count":1,
            "sessions":[
                {
                    "id": "a7a2b801-21db-4dae-a99b-4cbc0b81de96",
                    "vnc": false,
                    "screen": "1920x1080x24"
                 }
            ]
          },
          "jenkins-bot": {
            "count":6,
            "sessions":[
                //...
            ]
          }
        }
      },
      "firefox": {
        "1.56.1": {
          "jenkins-bot": {
            "count":3,
            "sessions":[
                //...
            ]
          }
        }
      }
    }
}

Users come from the authenticated identity — htpasswd or bearer token (embedded mode) or the X-Forwarded-User header (trusted-proxy mode). See Authentication and Authorization.

4.1.1. What Statistics Mean

  1. A new session request arrives to Selenwright.

  2. Selenwright -limit flag specifies how many sessions can be created simultaneously. It is shown as total in statistics. When requests reach the limit - subsequent requests are placed in queue. Queued requests just block and continue to wait.

  3. When there is a free slot for request Selenwright decides whether a Docker container or standalone driver process should be created. All requests during startup are marked as pending. Before proceeding to next step Selenwright waits for required port to be open.

  4. When a container or driver is started Selenwright does a new session request just in the same way as standard Selenium client.

  5. Depending on what is returned as response on the previous step session is marked as created or failed. Created and running sessions are also included to used value.

  6. When a session is created Selenwright just proxies the rest of session requests to the same container or driver.

  7. New session request can fail because of Selenium errors or issues with container / driver startup. In that case an error is returned to user.

For richer monitoring, enable the built-in Prometheus endpoint — see Metrics and Observability.

4.2. Uploading Files To S3

This feature only becomes available when Selenwright is compiled with s3 build tag (missing by default) as follows:

$ go build -tags s3

Selenwright can upload recorded video and log files for every running session to S3-compatible storage.

To enable this feature you need to specify S3 access information as follows:

$ ./selenwright -s3-endpoint https://s3.us-east-2.amazonaws.com -s3-region us-east-2 -s3-bucket-name my-bucket -s3-access-key <your-access-key> -s3-secret-key <your-secret-key>
If you omit -s3-access-key and -s3-secret-key flags then environment variables and shared credentials file are automatically tried as a fallback.

By default uploaded file name is preserved, i.e. S3 path is /<session-id>.log or /myCustomVideo.mp4. You can optionally provide your custom key pattern using -s3-key-pattern flag and a set of placeholders:

Table 4. S3 Key Placeholders
Placeholder Meaning

$browserName

Replaced by Selenium browser name capability value

$browserVersion

Replaced by Selenium browser version capability value

$date

Replaced by current date, e.g. 2018-11-01

$fileName

Replaced by source file name

$fileExtension

Replaced by source file extension

$fileType

Replaced by log for log files and video for video files

$platformName

Replaced by Selenium platform capability value

$quota

Replaced by the authenticated user/quota name

$sessionId

Replaced by Selenium session ID

For example, when launching Selenwright with -s3-key-pattern $browserName/$sessionId/log.txt files will be accessible as firefox/0ee0b48b-e29b-6749-b4f1-2277b8f8d6c5/log.txt. You can also override key pattern for every session with s3KeyPattern capability.

Sometimes you may want to upload only video files or files matching some complicated pattern or to not upload some files. To achieve this use -s3-include-files and -s3-exclude-files flags. These flags accept globs such as *.mp4.

4.3. Artifact History

Selenwright can track and persist session artifacts (logs, videos, browser downloads) with configurable retention. This enables operators to browse and retrieve artifacts from past sessions through the UI or API.

4.3.1. How It Works

  1. When a session ends, Selenwright creates a manifest containing session metadata (browser, version, protocol, user, timestamps).

  2. If the session produced artifacts (log files, video recordings), they are linked in the manifest.

  3. For Selenium sessions, browser downloads from /home/selenium/Downloads inside the container are captured via docker cp and stored alongside other artifacts. If the path does not exist (e.g. in Playwright images), the capture step is silently skipped.

  4. For Playwright sessions, file downloads are handled natively by the Playwright protocol — the file content is streamed to the client via WebSocket (page.download()), so there is nothing to capture from the container.

  5. A background janitor process runs every 3 hours and removes artifacts older than the configured retention period.

4.3.2. Configuration

Flag Description

-artifact-history-dir

Directory to store artifact history manifests and persisted downloads (default artifacts). Should be on a volume mount so data survives container recreation.

-artifact-history-settings

JSON file storing artifact history settings (default state/artifact-history.json). Survives restarts when placed on a volume mount.

4.3.3. Settings Endpoint

GET /history/settings

Returns the current artifact history configuration:

  • enabled — whether artifact history is active

  • retentionDays — how many days to keep artifacts (1—​365, default 7)

PUT /history/settings
Content-Type: application/json

{"enabled": true, "retentionDays": 14}

Updates artifact history settings. Changes take effect immediately.

This endpoint does not require authentication (open path).

4.3.4. Browsing Artifacts

Persisted downloads are served at:

GET /downloads/
GET /downloads/?json

The ?json parameter returns a JSON listing instead of the HTML directory view. Individual files can be downloaded directly by path.

Log files and videos are available at their respective endpoints (/logs/, /video/) as documented in Saving Session Logs and Video Recording.

4.4. Browser Discovery

Selenwright can automatically discover browser images available on the Docker host by scanning image labels. This allows operators to manage the browser catalog from the UI without manually editing browsers.json.

4.4.1. How It Works

  1. Selenwright scans all local Docker images for well-known labels that identify browser name and version.

  2. Images that are not yet in the catalog appear as "discovered" (unadopted).

  3. An admin user can adopt a discovered image to add it to the live browser catalog, or dismiss it to hide it from the list.

  4. Adoption state is persisted to adopted.json inside the -state-dir directory (default state/).

  5. After adoption or dismissal, Selenwright rebuilds the in-memory browser catalog automatically.

4.4.2. API Endpoints

All mutation endpoints require admin access (see Authentication and Authorization).

List Discovered Images
GET /browsers/discovered

Returns a JSON array of Docker images with browser labels that have not yet been adopted. Each entry contains:

  • name — browser name (e.g. "chrome", "firefox")

  • version — browser version

  • digest — image digest (used as identifier)

  • repoTags — image repository tags

Adopt an Image
POST /browsers/adopt
Content-Type: application/json

{"digest": "<image-digest>"}

Adds the image to the browser catalog and triggers a catalog rebuild.

Dismiss an Image
POST /browsers/dismiss
Content-Type: application/json

{"digest": "<image-digest>"}

Removes the image from the adopted set and triggers a catalog rebuild.

Rescan Images
POST /browsers/rescan

Triggers a manual rescan of Docker images and rebuilds the catalog from the current adopted set. Returns the list of unadopted images.

4.4.3. Configuration

Flag Description

-state-dir

Directory for persistent state including adopted.json (default state). Created on startup if missing.

4.4.4. Reloading

Sending SIGHUP to the Selenwright process also triggers a catalog rebuild from the current adopted set.

4.5. Docker Compose Stack Management

When Selenwright runs as part of a Docker Compose deployment, it can inspect, pull, and recreate the stack services through a set of API endpoints. This allows the operator UI to manage updates without SSH access to the host.

4.5.1. How It Works

Selenwright detects its own Compose deployment by reading Docker container labels (com.docker.compose.project, com.docker.compose.service, etc.) from its own container. When these labels are present, the stack management endpoints become available.

4.5.2. API Endpoints

Stack Status
GET /stack/status

Returns the current state of the Compose stack:

  • available — whether Selenwright is running inside a detected Compose stack

  • reason — explanation when stack management is not available

  • projectName — Docker Compose project name

  • services — array of service objects, each containing:

    • service — service name

    • image — image reference

    • imageId / imageIdShort — full and short image digest

    • containerId — running container ID

    • status — container status

    • created — container creation timestamp

Pull Images
POST /stack/pull

Pulls the latest images for all stack services. Returns per-service results:

  • service — service name

  • image — image reference

  • previousId — image digest before pull

  • currentId — image digest after pull

  • updated — true when a new image was pulled

Recreate Stack
POST /stack/recreate

Recreates the Docker Compose stack using docker compose up -d. This applies any newly pulled images. Returns the output from the compose command.

The pull and recreate operations use a helper container (docker:27-cli) to run Docker Compose commands with access to the host Docker socket.

4.6. Metrics and Observability

4.6.1. Prometheus Metrics

Selenwright can expose a Prometheus-compatible metrics endpoint for monitoring session usage and server health.

./selenwright -enable-metrics
Flag Description

-enable-metrics

Expose the metrics endpoint (disabled by default)

-metrics-path

Path for the metrics endpoint (default /metrics)

Exposed metrics:
  • Queue depth and pending requests

  • Active session count

  • Session duration histogram

  • Authentication failure counter (by auth mode)

  • Capability policy rejection counter

The metrics endpoint is not gated by the configured authenticator. It is expected to live behind the same network boundary as the Prometheus scraper.

4.6.2. Structured Logging

./selenwright -log-json

When -log-json is set, Selenwright emits logs as one JSON object per line with fields: event, request_id, fields, level, time. Default is the legacy bracketed text format.

4.6.3. Event Workers

./selenwright -event-workers=16

The -event-workers flag (default 16) controls the number of goroutines that dispatch session-lifecycle events (FileCreated, SessionStopped) to registered listeners such as S3 upload handlers. Bounds fan-out so a single slow listener cannot leak goroutines.

4.7. Saving Session Metadata

This feature only becomes available when Selenwright is compiled with metadata build tag (missing by default).

Selenwright can save session metadata to a separate JSON file. Main use case is to somehow post-process session logs, e.g. send them to map-reduce cluster. No additional configuration is needed to enable this feature. When enabled and -log-output-dir flag is set - Selenwright will automatically save JSON files <log-output-dir>/<session-id>.json with the following content:

Example metadata JSON file
{
    "id": "62a4d82d-edf6-43d5-886f-895b77ff23b7",
    "capabilities": {
        "browserName": "chrome",
        "version": "70.0",
        "name": "MyCoolTest",
        "screenResolution": "1920x1080x24"
    },
    "started": "2018-11-15T16:23:12.440916+03:00",
    "finished": "2018-11-15T16:23:12.480928+03:00"
}

5. Security

5.1. Authentication and Authorization

Selenwright controls who can create browser sessions and what they can do. There are two roles: regular user and admin. By default authentication is enabled and requires a password file.

5.1.1. Quick Start: Adding Users

Selenwright uses a standard htpasswd file with bcrypt passwords. Create it with Docker:

docker run --rm httpd:alpine htpasswd -nbB alice MyPassword123 >> users.htpasswd
docker run --rm httpd:alpine htpasswd -nbB bob AnotherPass456 >> users.htpasswd

This creates a file users.htpasswd with two users: alice and bob. To add more users later, run the same command again — it appends to the file.

Pass the -htpasswd flag pointing to this file. As a binary:

./selenwright -htpasswd users.htpasswd

As a Docker container — mount the file and pass the flag:

docker run -d --name selenwright             \
  -p 4444:4444                               \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v $(pwd)/users.htpasswd:/etc/selenwright/users.htpasswd:ro \
  selenwright/hub:latest-release        \
  -htpasswd /etc/selenwright/users.htpasswd

Or in Docker Compose:

services:
  selenwright:
    image: selenwright/hub:latest-release
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./users.htpasswd:/etc/selenwright/users.htpasswd:ro
    command: ["-htpasswd", "/etc/selenwright/users.htpasswd"]
    ports:
      - "4444:4444"

Now alice and bob can create sessions using HTTP Basic Auth:

curl -u alice:MyPassword123 http://localhost:4444/status

5.1.2. API Tokens (for Machine Clients)

htpasswd passwords are the right fit for humans logging into the UI. For Playwright/Selenium clients running in CI or on a developer’s laptop, selenwright supports bearer tokens so credentials never need to be pasted into a URL (where they leak into shell history, logs, and screenshots).

An admin mints a token on behalf of a user, the user drops it into SELENWRIGHT_TOKEN (or however the client reads secrets), and the client sends Authorization: Bearer <token> with every request. The token inherits the owner’s groups at creation time, so group-based session ACL still applies.

Tokens are stored hashed (bcrypt) in <state-dir>/auth/tokens.json. Plaintext is shown to the admin once — distribute it through a secret manager (1Password, Bitwarden, Keybase). Never paste tokens into Slack, tickets, or email.

Getting Your First Admin Token

The curl in the next section authenticates with Authorization: Bearer <admin-token>. The very first admin token is produced by selenwright itself — where exactly depends on how you started the process:

Started with…​ First admin token comes from…​

-htpasswd users.htpasswd (production)

No token is auto-generated. Log into the UI with an htpasswd account listed in -admin-users. Basic-auth login establishes a session cookie; while logged in, open Settings → API Tokens and create a token for yourself (or call POST /api/admin/tokens with the cookie). From then on this bearer is what automation uses — the htpasswd password stays in your password manager.

SELENWRIGHT_AUTH_TOKEN=<value> (declarative / CI)

The env value itself is your admin token. On first boot (empty token store) selenwright hashes it and persists it as owner=admin name=env-seed. Later boots with the same value are a no-op; a different value logs a warning and keeps existing tokens — wipe <state-dir>/auth/tokens.json to re-seed.

Neither -htpasswd nor the env var (dev fallback)

Selenwright generates one on first boot and prints it to stdout (example below). Save it — it will not be shown again. Lost it? Delete <state-dir>/auth/tokens.json and restart to get a fresh one.

Dev-fallback stdout:

./selenwright -listen :4444
# [-] [INIT] [No htpasswd configured — generated initial admin token]
# [-] [INIT] ┌──────────────────────────────────────────────────────────┐
# [-] [INIT] │  swr_live_4f8a9c2e7b1d3f5e8a6c9b4d7e2f1a3c
# [-] [INIT] │  id=tok_... owner=admin name=initial
# [-] [INIT] │  Save it — won't be shown again.
# [-] [INIT] │  Hash stored at state/auth/tokens.json
# [-] [INIT] └──────────────────────────────────────────────────────────┘
Minting Tokens

Once the admin has a bearer token (see above), mint tokens for users via REST:

curl -X POST https://selen.corp.com/api/admin/tokens \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{"owner":"alice","name":"laptop"}'
# → {"id":"tok_...","token":"swr_live_..."}

The same operation is available in the UI: log in as an admin, open Settings → API Tokens, click Create, pick the owner and a human-readable name (e.g. laptop-alice, ci-runner). Copy the plaintext from the modal — it will not be shown again.

Using Tokens

Playwright:

const browser = await chromium.connect({
  wsEndpoint: 'wss://selen.corp.com/playwright/chromium/1.56.1',
  headers: { Authorization: `Bearer ${process.env.SELENWRIGHT_TOKEN}` },
});

Selenium (ClientConfig in Java; equivalents in every WebDriver binding):

ClientConfig config = ClientConfig.defaultConfig()
    .baseUri(URI.create("https://selen.corp.com/wd/hub"))
    .addHeader("Authorization", "Bearer " + System.getenv("SELENWRIGHT_TOKEN"));

For WebSocket clients that cannot set custom headers (wscat, <img src>, some browser DevTools frontends), selenwright accepts a ?token=<plaintext> query parameter on WebSocket-upgrade paths only: /playwright/, /wd/hub/, /devtools/, /vnc/, /logs/. The parameter is stripped from the URL before the request reaches upstream handlers, so it does not land in container logs. URL-embedded credentials still leak into shell history, reverse-proxy access logs, and terminal scrollback — prefer the header form whenever the client supports it.

Revoking Tokens

A single token:

curl -X DELETE https://selen.corp.com/api/admin/tokens/<id> \
  -H "Authorization: Bearer <admin-token>"

Every token owned by a user (offboarding):

curl -X DELETE 'https://selen.corp.com/api/admin/tokens?owner=alice' \
  -H "Authorization: Bearer <admin-token>"

The UI exposes both operations on the same page. Revocation is immediate — in-flight requests that already passed the auth check continue to their natural end, but no new request will clear.

5.1.3. Making a User an Admin

By default all users are regular (non-admin). To promote users to admin, list them in -admin-users:

./selenwright -htpasswd users.htpasswd -admin-users=alice

Now alice is admin, bob is a regular user. Admins can:

  • Access any user’s sessions (VNC, logs, devtools)

  • Use restricted capabilities (env, dnsServers, hostsEntries, additionalNetworks, applicationContainers)

  • Adopt and dismiss browsers in the Browser Discovery UI

5.1.4. Updating Users Without Restart

Edit the htpasswd file (add/remove/change passwords), then send SIGHUP:

docker kill -s HUP selenwright

Selenwright reloads the file without dropping running sessions.

5.1.5. Disabling Authentication

You can disable auth entirely with either spelling:

./selenwright -auth-mode=none -listen=127.0.0.1:4444
./selenwright --no-auth       -listen=127.0.0.1:4444

--no-auth is a convenience alias for -auth-mode=none. Both are accepted on any listen address, including :4444. When the listen address is not a loopback interface, selenwright logs a warning that the service is reachable without authentication — the operator is responsible for network-level protection (firewall, overlay network, bastion, reverse proxy).

5.1.6. What Is Reachable Anonymously

When authentication is enabled (-auth-mode=embedded or -auth-mode=trusted-proxy), these endpoints are deliberately left open and do not require a credential:

Path Purpose

GET /ping

Liveness probe. Returns {"uptime":…​, "version":…​} — no session or user data

GET /error

Selenium WebDriver invalid-session error JSON. Used as a proxy fall-through target

GET /

Plain-text build banner (e.g. You are using Selenwright <git-rev>!)

GET /whoami

Returns the authenticated identity or {"authenticated":false,"authMode":"embedded"} for anonymous callers. Required so the UI can decide whether to render the login page

POST /login, POST /logout

Embedded-mode cookie login/logout

GET /metrics (only when -enable-metrics is set)

Prometheus scrape target. Listed in a separate open-paths set so it tracks the flag and stays gated otherwise

Everything else — /status, /config, /history/settings, /downloads/, /stack/status, /wd/hub/, /playwright/, /vnc/, /video/, /logs/, /download/, /clipboard/, /devtools/, the admin token REST surface — requires a valid BasicAuth header, bearer token, or session cookie. An anonymous scrape of /status was previously possible and leaked live session IDs, container IPs, and owner quotas; it now returns 401.

Human-readable Prometheus/Grafana setups still work: Prometheus scrapes /metrics (the open one), which carries lifecycle counters and queue depth gauges but none of the per-session detail that /status exposes.

5.1.7. Authentication Modes

Controlled by -auth-mode (default: embedded).

embedded — Built-in BasicAuth

The default mode described above. Uses an htpasswd file with bcrypt hashes.

Flag Description

-htpasswd (required)

Path to the bcrypt htpasswd file

-admin-users

Comma-separated list of admin usernames

-groups-file

Path to JSON group file (see Team/Group Sharing). Hot-reloaded on SIGHUP

trusted-proxy — Behind a Reverse Proxy

Use this when an upstream proxy (nginx, Envoy, OAuth2 Proxy) handles authentication and passes the user identity via HTTP headers.

./selenwright \
  -auth-mode=trusted-proxy \
  -user-header=X-Forwarded-User \
  -admin-header=X-Admin
Flag Description

-user-header (default X-Forwarded-User)

Header containing the authenticated username

-admin-header (default X-Admin)

Header whose value "true" marks the caller as admin

-groups-header (default X-Groups)

Header containing a comma-separated list of group names (see Team/Group Sharing). Set to empty to disable group reading

Without source trust validation (see below), any client can forge these headers — including -groups-header, which would let a caller escalate into any team’s sessions. Always combine with at least one source trust check.
none — No Authentication

All requests are anonymous non-admin. Allowed on any listen address; the operator is responsible for network-level protection (firewall, overlay, bastion). A warning is logged when the listen address is not loopback.

5.1.8. Source Trust (Protecting the Proxy)

When using trusted-proxy mode, you need to ensure that only your proxy can reach Selenwright — otherwise anyone can set the X-Forwarded-User header. Source trust adds that protection. When multiple checks are configured, all must pass.

Flag What it checks

-trusted-proxy-secret=mysecret

Request must have X-Router-Secret: mysecret header

-trusted-proxy-cidr=10.0.0.0/8

Request source IP must be in the listed CIDR range

-trusted-proxy-mtls-ca=/path/to/ca.pem

Request must present a client certificate signed by this CA

Example: proxy + shared secret + CIDR
./selenwright \
  -auth-mode=trusted-proxy \
  -user-header=X-Forwarded-User \
  -admin-header=X-Admin \
  -trusted-proxy-secret=s3cret \
  -trusted-proxy-cidr=10.0.0.0/8

Source trust configuration is reloaded on SIGHUP.

5.1.9. Capability Policy

Controlled by -caps-policy (default: strict).

Some capabilities give the user control over the container environment. Under strict policy, these are admin-only:

  • env — environment variables

  • dnsServers — custom DNS servers

  • hostsEntries — custom /etc/hosts entries

  • additionalNetworks — extra Docker networks

  • applicationContainers — sidecar containers

Regular users requesting these get a 400 error. Set -caps-policy=permissive to allow all users to use all capabilities (legacy behavior).

5.1.10. Session Ownership

Every session is bound to the user who created it:

  • Owner — full access to their own sessions (VNC, logs, devtools, downloads, terminate)

  • Group-mate — when the owner and the caller belong to at least one common group, the caller gets the same full access as the owner (see Team/Group Sharing)

  • Admin — full access to all sessions

  • Other users — 403 Forbidden

Ownership is enforced uniformly for both Selenium (DELETE /wd/hub/session/{id}) and Playwright (DELETE /playwright/session/{id}) termination, as well as for VNC, devtools, downloads, clipboard and live log endpoints.

5.1.11. Team/Group Sharing

The default user == owner rule is too narrow when sessions are created by a shared service account — for example, jenkins-bot launching tests from CI. Without groups, no human user can terminate those sessions through the UI except the admin.

Groups solve this. Members of the same group can manage each other’s sessions (including sessions owned by service accounts in the group). Admin behavior is unchanged.

Configuring Groups (embedded mode)

Supply a JSON file mapping group name to list of member usernames:

{
  "qa-payments": ["alice", "bob", "jenkins-bot"],
  "qa-growth":   ["carol"]
}

Point to it with -groups-file:

./selenwright \
  -htpasswd users.htpasswd \
  -admin-users=root \
  -groups-file=groups.json

The file is hot-reloaded on SIGHUP alongside the htpasswd file. Parse or validation errors are logged and the previous membership is kept.

Configuring Groups (trusted-proxy mode)

The upstream proxy passes the user’s groups as a comma-separated list in the -groups-header header (default X-Groups):

./selenwright \
  -auth-mode=trusted-proxy \
  -user-header=X-Forwarded-User \
  -admin-header=X-Admin \
  -groups-header=X-Groups \
  -trusted-proxy-secret=s3cret

Example request from a trusted proxy:

X-Forwarded-User: alice
X-Groups: qa-payments,qa-growth

Set -groups-header="" to disable group reading entirely. X-Groups is always stripped from incoming requests before the application handlers run (same treatment as X-Forwarded-User and X-Admin), so downstream clients cannot forge it — provided at least one source trust check is configured.

Snapshot Semantics

The groups of a session’s owner are captured at session creation time and stored on the session itself. Revoking group membership (editing the JSON file or changing the upstream’s X-Groups output) does not retroactively change ACL for sessions that are already running. New sessions created after the change see the new membership immediately.

This avoids race conditions mid-operation: a group-mate who is in the middle of terminating a session cannot lose access half-way through.

6. Configuration

6.1. Docker Settings

6.1.1. Container Resource Limits

You may want to limit memory and CPU consumption for each browser container. Use -mem and -cpu flags:

./selenwright -mem 128m -cpu 1.0

Values are specified in Docker format. -mem accepts units like 128m or 1g. -cpu is a float representing the number of CPUs per container.

These can also be set per-browser in the browser catalog — see Browsers Configuration File.

6.2. Browsers Configuration File

Selenwright can be configured with an optional JSON file that defines available browsers. When Browser Discovery is used, this file is not required — the browser catalog is built from adopted Docker image labels. The file serves as a fallback when no adopted images are found.

browsers.json
{
    "firefox": {                                     (1)
      "default": "46.0",                             (2)
      "versions": {                                  (3)
        "46.0": {                                    (4)
          "image": "example.com/firefox:46.0",       (5)
          "port": "4444",                            (6)
          "tmpfs": {"/tmp": "size=512m"},            (7)
          "path" : "/wd/hub",                        (8)
          "protocol" : "webdriver",                  (9)
          "volumes" : ["/from:/to:ro"],              (10)
          "env" : ["TZ=Europe/Berlin"],              (11)
          "hosts" : ["example.com:192.168.0.1"],     (12)
          "shmSize" : 268435456,                     (13)
          "cpu" : "1.0",                             (14)
          "mem" : "512m",                            (15)
        },
        "50.0" :{
            // ...
        }
      }
    },
    "chrome": {
        // ...
    }
}
1 Browser name
2 Default browser version
3 A list of available browser versions
4 Version name
5 Image name or driver binary command
6 Containers only. Port to proxy connections to, see below
7 Optional. Containers only. Add in-memory filesystem (tmpfs) to container, see below
8 Optional. Path relative to / where we request a new session, see below
9 Optional. Upstream protocol contract. Omit this field for default WebDriver behavior or set it to playwright for native Playwright images.
10 Optional. Containers only. Mount path from host machine to created container
11 Optional. Environment variables to be passed to browser container or driver process
12 Optional. Custom /etc/hosts entries to be passed to browser container in hostname:ip format.
13 Optional. Shared memory size in bytes to be set for container. Some browsers (e.g. Chrome) may work unstable when having insufficient shmSize. Default value is 256 megabytes.
14 Optional. Containers only. Container CPU limit in Docker units.
15 Optional. Containers only. Container memory limit in Docker units.

This file represents a mapping between a list of supported browser versions and Docker container images or driver binaries.

To use custom browsers configuration file - use -conf flag:

$ ./selenwright -conf /path/to/browsers.json

6.2.1. Browser Name and Version

Browser name and version are just strings that are matched against Selenium desired capabilities:

  • browserName

  • version

For native Playwright connections the same entries are selected from the request path:

  • <browser> in /playwright/<browser>/<playwright-version>

  • <playwright-version> in /playwright/<browser>/<playwright-version>

If no version capability is present default version is used. When there is no exact version match we also try to match by prefix. That means version string in JSON should start with version string from capabilities.

Example 1. Matching Logic
in browsers.json
"versions": {
   "46.0": {

Version capability that will match:

version = 46 (46.0 starts with 46)

Will not match:

version = 46.1 (46.0 does not start with 46.1)

6.2.2. Image

Image by default is a string with container specification in Docker format (hub.example.com/project/image:tag).

Image must be already pulled.

Example 2. Valid images

Pulled from internal docker registry:

my-internal-docker-hub.example.com/playwright-chromium:latest

Pulled from hub.docker.com

selenwright/playwright-chromium:latest

Standalone Binary

If you wish to use a standalone binary instead of Docker container, then image field should contain command specification in square brackets:

"46.0": {
    "image": ["/usr/bin/mybinary", "-arg1", "foo", "-arg2", "bar", "-arg3"],
    "port": "4444"
},

Selenwright proxies connections to either Selenium server or standalone driver binary. Depending on operating system both can be packaged inside Docker container.

6.2.3. Other Optional Fields

"46.0": {
    //...
    "port": "",
    "tmpfs": {"/tmp": "size=512m", "/var": "size=128m"},
    "path" : "",
    "protocol" : "",
    "volumes": ["/from:/to", "/another:/one:ro"],
    "env" : ["TZ=Europe/Berlin", "ONE_MORE_VARIABLE=itsValue"],
    "hosts" : ["one.example.com:192.168.0.1", "two.example.com:192.168.0.2"],
    "labels" : {"component": "frontend", "project": "my-project"},
    "sysctl" : {"net.ipv4.tcp_timestamps": "2", "kern.maxprocperuid": "1000"},
    "shmSize" : 268435456,
},
  • port (only for containers) - You should use port field to specify the real port inside container that container process (Selenium server, Selenwright or driver) will listen on. For native Playwright images set this to the Playwright server listener port.

  • tmpfs (optional) - You may probably know that moving browser cache to in-memory filesystem (Tmpfs) can dramatically improve its performance. Selenwright can automatically attach one or more in-memory filesystems as volumes to Docker container being run. To achieve this define one or more mount points and their respective sizes in optional tmpfs field.

  • path (optional) - path field is needed to specify relative path to the URL where a new session is created (default is /). Which value to specify in this field depends on container contents. For example, most of Firefox containers have Selenium server inside - thus you need to specify /wd/hub. Chrome and Opera containers use web driver binary as entrypoint application which is accepting requests at /. Playwright images also default to / when the field is omitted, and the selected WebSocket path should stay fixed for a given image.

  • protocol (optional) - protocol selects the upstream contract used by Selenwright. Two values are supported:

    • webdriver (default when omitted) — standard Selenium WebDriver protocol. Used for Chrome, Firefox, Opera and other Selenium-compatible images.

    • playwright — native Playwright WebSocket protocol. Set this when the image runs a Playwright server. Clients connect via /playwright/<browser>/<version> WebSocket endpoint instead of the Selenium HTTP API.

  • volumes (optional) - This field allows to mount volumes from host machine to browser container. Should be specified as an array of Docker volume expressions: /host/dir:/container/dir[:mode].

  • env (optional) - This field allows to set any environment variables in running container. Specified as an array of NAME=value pairs.

  • hosts (optional) - This field allows to add custom /etc/hosts entries to running container. Specified as an array of hostname:ip pairs.

  • labels (optional) - This field allows to add custom labels to running container. Specified as an object of "key": "value" pairs.

  • sysctl (optional) - This field allows to adjust kernel parameters of running container. Specified as an object of "key": "value" pairs.

  • shmSize (optional) - Use it to override shared memory size for browser container.

  • publishAllPorts (optional) - When set to true, publishes all exposed container ports to the host. Useful for debugging or when browser containers need to be accessed directly.

6.3. Logging Configuration File

By default Docker container logs are saved to host machine hard drive. When using Selenwright for local development that’s ok. But in big Selenium cluster you may want to send logs to some centralized storage like Logstash or Graylog. Docker provides such functionality by so-called logging drivers. An optional Selenwright logging configuration file allows to specify which logging driver to use globally for all started Docker containers with browsers. Configuration file has the following format:

config/container-logs.json
{
    "Type" : "<driver-type>",   (1)
    "Config" : {                (2)
      "key1" : "value1",
      "key2" : "value2"
    }
}
1 is a supported Docker logging driver type like syslog, journald or awslogs.
2 Config is a list of key-value pairs used to configure selected driver.

For example these Docker logging parameters:

--log-driver=syslog --log-opt syslog-address=tcp://192.168.0.42:123 --log-opt syslog-facility=daemon

... are equivalent to the following Selenwright logging configuration:

config/container-logs.json
{
    "Type" : "syslog",
    "Config" : {
      "syslog-address" : "tcp://192.168.0.42:123",
      "syslog-facility" : "daemon"
    }
}

To specify custom logging configuration file - use -log-conf flag:

$ ./selenwright -log-conf /path/to/logging.json ...

6.4. Reloading Configuration

To reload configuration without restart send SIGHUP:

# kill -HUP <pid>
# docker kill -s HUP <container-id-or-name>
Use only one of these commands.

To check Selenwright instance health use /ping:

Request
$ curl -s http://example.com:4444/ping
Result
{
    "uptime": "2m46.854829503s",
    "lastReloadTime": "2026-04-16T12:33:06+03:00",
    "numRequests": 42,
    "version": "HEAD"
}

It returns 200 OK when Selenwright operates normally. Fields: server uptime, last configuration reload time (RFC3339), total session requests since startup, and build version.

6.5. Updating Browsers

  1. Pull new browser images on the host:

    $ docker pull selenwright/playwright-chromium:latest
  2. Open the Browser Discovery UI and adopt the new images.

  3. Selenwright rebuilds the in-memory catalog automatically — no restart needed.

6.5.2. Using Stack Management

When running via Docker Compose, use the Docker Compose Stack Management endpoints (or the UI) to pull updated images and recreate the stack.

6.5.3. Manual browsers.json

  1. Edit browsers.json and pull the new images.

  2. Reload without restart:

    $ docker kill -s HUP selenwright

    See Reloading Configuration for details.

6.6. Setting Timezone

When used in Docker container Selenwright will have timezone set to UTC. To set custom timezone pass TZ environment variable to Docker:

$ docker run -d --name selenwright                     \
    -p 4444:4444                                    \
    -e TZ=Europe/Berlin                             \
    -v /var/run/docker.sock:/var/run/docker.sock    \
    selenwright/hub:latest-release
This sets the timezone for the Selenwright process itself (affects log timestamps, /ping response, etc.). To set timezone per browser session, use the timeZone capability — see Per-session Time Zone: timeZone.

6.7. Selenwright with Docker Compose

In example Docker Compose configurations below we assume that you created directories for video and log storage:

$ mkdir -p /data/selenwright/video /data/selenwright/logs
A browsers.json file is not required — Selenwright discovers browser images via Docker labels. See Browser Discovery. If you prefer a manual catalog, add -conf /etc/selenwright/browsers.json to the command and mount the config directory.

6.7.1. Option 1. Running Selenwright in default Docker network

In that case bridge network mode should be used for all services:

version: '3'
services:
  selenwright:
    network_mode: bridge
    image: selenwright/hub:latest-release
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "/data/selenwright/video:/opt/selenwright/video"
      - "/data/selenwright/logs:/opt/selenwright/logs"
    environment:
      - OVERRIDE_VIDEO_OUTPUT_DIR=/data/selenwright/video
    command: ["-video-output-dir", "/opt/selenwright/video", "-log-output-dir", "/opt/selenwright/logs"]
    ports:
      - "4444:4444"

6.7.2. Option 2. Running Selenwright in custom Docker network

In that case you have to add -container-network flag to Selenwright as follows:

  1. Create custom Docker network:

    $ docker network create selenwright
  2. Start Selenwright:

version: '3'
networks:
  selenwright:
    external:
      name: selenwright # This assumes network is already created
services:
  selenwright:
    networks:
      selenwright: null
    image: selenwright/hub:latest-release
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "/data/selenwright/video:/opt/selenwright/video"
      - "/data/selenwright/logs:/opt/selenwright/logs"
    environment:
      - OVERRIDE_VIDEO_OUTPUT_DIR=/data/selenwright/video
    command: ["-video-output-dir", "/opt/selenwright/video", "-log-output-dir", "/opt/selenwright/logs", "-container-network", "selenwright"]
    ports:
      - "4444:4444"

6.8. Log Files

6.8.1. Log Format

By default Selenwright uses a bracketed text format:

2026/04/16 12:34:56 [41301] [SESSION_CREATED] [alice] [a7a2b801-...] [chrome:130.0] [1] [2.15s]

Every line contains:

Field Description

Time

Timestamp in server timezone

Request counter

[41301] — numeric ID linking all log lines for the same request. Useful because session ID is unknown during early stages.

Status

[SESSION_CREATED] — event type (see below)

User / quota

Authenticated username

Session ID

Unique session identifier

Browser

Name and version

Duration

Time taken for the operation

Not all fields are present in every line — it depends on the event type.

6.8.2. JSON Format

Pass -log-json to emit structured logs:

{"event":"SESSION_CREATED","request_id":41301,"fields":{"user":"alice","session":"a7a2b801-..."},"level":"info","time":"2026-04-16T12:34:56+02:00"}

6.8.3. Common Log Statuses

Session lifecycle: NEW_REQUEST, NEW_REQUEST_ACCEPTED, SESSION_ATTEMPTED, SESSION_CREATED, SESSION_DELETED, SESSION_TIMED_OUT, SESSION_FAILED, CLIENT_DISCONNECTED

Container management: CREATING_CONTAINER, STARTING_CONTAINER, CONTAINER_STARTED, CONTAINER_REMOVED, SERVICE_STARTED, SERVICE_STARTUP_FAILED

Playwright: PLAYWRIGHT_SESSION_CREATED, PLAYWRIGHT_SESSION_STOPPED, PLAYWRIGHT_CLIENT_DISCONNECTED, PLAYWRIGHT_UPSTREAM_DISCONNECTED

Features: VNC_ENABLED, DEVTOOLS_*, DOWNLOADING_FILE, UPLOADED_FILE, VIDEO_ERROR, LOG_ERROR

Discovery: DISCOVERY — browser image scanning, adopt, dismiss, catalog rebuild

Server: INIT, SIGHUP, SHUTTING_DOWN

6.9. Selenwright CLI Flags

The following flags are supported by selenwright command.

6.9.1. Server & Network

-listen string
    Network address to accept connections (default ":4444")
-allowed-origins string
    Comma-separated list of allowed Origin values for WebSocket upgrades
    (devtools, playwright, vnc, logs). Empty (default) keeps permissive
    behavior; '*' is explicit allow-all. Recommended: configure to your
    CI/QA hosts to defend against Cross-Site WebSocket Hijacking

6.9.2. Authentication

-auth-mode string
    Authentication mode: 'embedded', 'trusted-proxy', or 'none'
    (default "embedded"). 'none' is allowed on any listen address;
    operator owns network-level protection. See
    <<Authentication and Authorization>> for details
--no-auth
    Alias for -auth-mode=none. Allowed on any listen address; operator
    owns network-level protection
-htpasswd string
    Path to bcrypt-format htpasswd file used by -auth-mode=embedded.
    Generate with: htpasswd -B users.htpasswd alice. When omitted and
    auth is enabled, selenwright boots in dev mode and prints a single
    admin bearer token to stdout; pin it across restarts with the
    SELENWRIGHT_AUTH_TOKEN env var
-admin-users string
    Comma-separated list of usernames treated as admin in
    -auth-mode=embedded
-user-header string
    Header to read for authenticated user identity in
    -auth-mode=trusted-proxy (default "X-Forwarded-User")
-admin-header string
    Header in -auth-mode=trusted-proxy whose value 'true' marks the
    request as administrative (default "X-Admin")
-groups-file string
    Path to JSON file mapping team/group name to list of member
    usernames, e.g. {"qa-payments":["alice","jenkins-bot"]}. Members of
    the same group can manage each other's sessions (useful for CI
    service accounts). Hot-reloaded on SIGHUP. Used with
    -auth-mode=embedded
-groups-header string
    Header to read CSV group names from in -auth-mode=trusted-proxy
    (e.g. "qa-payments,qa-growth"). Empty disables reading groups from
    headers (default "X-Groups")
-caps-policy string
    Capability policy: 'strict' rejects dangerous caps (env, dnsServers,
    hostsEntries, additionalNetworks, applicationContainers) for
    non-admin callers; 'permissive' preserves legacy behavior
    (default "strict")

6.9.3. Source Trust (Upstream Proxy Validation)

When set, these checks are AND’d together — all configured conditions must pass.

-trusted-proxy-secret string
    Shared secret expected in X-Router-Secret header. Every request must
    present this value or it is rejected with 401
-trusted-proxy-cidr string
    Comma-separated CIDR allow-list for the source IP (e.g.
    "10.0.0.0/8,192.168.1.0/24")
-trusted-proxy-mtls-ca string
    Path to PEM bundle of CAs that issued the trusted client certificate.
    Requires a verified mTLS client certificate on every request

6.9.4. Session Management

-limit int
    Simultaneous container runs (default 5)
-timeout duration
    Session idle timeout in time.Duration format (default 1m0s)
-max-timeout duration
    Maximum valid session idle timeout in time.Duration format
    (default 1h0m0s)
-session-attempt-timeout duration
    New session attempt timeout in time.Duration format (default 30s)
-session-delete-timeout duration
    Session delete timeout in time.Duration format (default 30s)
-service-startup-timeout duration
    Service startup timeout in time.Duration format (default 30s)
-retry-count int
    New session attempts retry count (default 1)
-graceful-period duration
    Graceful shutdown period in time.Duration format (default 5m0s)
-disable-queue
    Disable wait queue

6.9.5. Browser Configuration

-conf string
    Browsers configuration file (default "config/browsers.json")
-log-conf string
    Container logging configuration file
-default-enable-vnc
    Default value for the enableVNC capability when a client does not
    pass enableVNC. Clients can still opt out per session

6.9.6. Container Runtime

-disable-docker
    Disable docker support (driver-only mode)
-container-network string
    Network to be used for containers (default "default")
-browser-network string
    Dedicated Docker network for browser containers. Created on startup
    with Internal=true (no external gateway) to limit blast radius of
    sandbox escapes. Set empty to disable isolation and revert to
    -container-network (default "selenwright-browsers")
-privileged
    Run browser containers in privileged mode (default false)
-cap-add-sys-admin
    Add the SYS_ADMIN Linux capability to browser containers without
    full -privileged. Chrome's user-namespace sandbox requires it
-capture-driver-logs
    Whether to add driver process logs to Selenwright output
-mem value
    Containers memory limit e.g. 128m or 1g
-cpu value
    Containers cpu limit as float e.g. 0.2 or 1.0

6.9.7. Video Recording

-video-output-dir string
    Directory to save recorded video to (default "video")
-video-recorder-image string
    Image to use as video recorder
    (default "selenwright-video-recorder:latest-release")

6.9.8. Logs

-log-output-dir string
    Directory to save session log to
-save-all-logs
    Whether to save all logs without considering capabilities
-log-json
    Emit logs as one JSON object per line (event, request_id, fields,
    level, time). Default is the legacy bracketed text format

6.9.9. Artifact History

-artifact-history-dir string
    Directory to store artifact history manifests and persisted downloads
    (default "artifacts")
-artifact-history-settings string
    JSON file storing artifact history settings (enabled, retentionDays)
    (default "state/artifact-history.json")
-state-dir string
    Directory for persistent state such as adopted browser set. Created
    on startup if missing (default "state")

6.9.10. File Upload

-enable-file-upload
    File upload support

See Uploading Files To Browser for the POST /file request format and size limits.

6.9.11. Request Limits

-max-create-body-bytes int
    Maximum POST body size for /session create requests in bytes
    (default 4194304 / 4 MiB)
-max-upload-body-bytes int
    Maximum POST body size for /file upload requests in bytes
    (default 268435456 / 256 MiB)
-max-upload-extracted-bytes int
    Maximum total extracted size for /file uploaded zip archives in bytes
    (default 1073741824 / 1 GiB)
-max-ws-message-bytes int
    Maximum single WebSocket message size in bytes for Playwright,
    DevTools, VNC and log streams. 0 disables the limit
    (default 67108864 / 64 MiB)

6.9.12. Metrics & Observability

-enable-metrics
    Expose a Prometheus-compatible /metrics endpoint (queue depth,
    session counts, duration histogram, auth/caps rejection counters)
-metrics-path string
    Path the Prometheus metrics endpoint is served on (default "/metrics")
-event-workers int
    Number of worker goroutines that dispatch session-lifecycle events
    (FileCreated, SessionStopped) (default 16)

6.9.13. Misc

-version
    Show version and exit

6.9.14. Environment Variables

Variable Purpose

SELENWRIGHT_AUTH_TOKEN

Plaintext bearer token to seed on first boot. Applied only when <state-dir>/auth/tokens.json is absent or empty; subsequent boots ignore it. Stored as owner=admin name=env-seed. If the env value does not match what is already on disk, selenwright logs a warning and leaves the stored tokens untouched. See API Tokens (for Machine Clients)

6.9.15. Usage Example

As a binary:

$ ./selenwright -limit 10

As a Docker container:

$ docker run -d --name selenwright                  \
    -p 4444:4444                                    \
    -v /var/run/docker.sock:/var/run/docker.sock    \
    selenwright/hub:latest-release             \
    -limit 10

6.9.16. S3 CLI Flags

The following flags are supported by selenwright command when compiled with S3 support:

-s3-access-key string
    S3 access key
-s3-bucket-name string
    S3 bucket name
-s3-endpoint string
    S3 endpoint URL
-s3-exclude-files string
    Pattern used to match and exclude files
-s3-force-path-style
    Force path-style addressing for file upload
-s3-include-files string
    Pattern used to match and include files
-s3-keep-files
    Do not remove uploaded files
-s3-key-pattern string
    S3 bucket name (default "$fileName")
-s3-reduced-redundancy
    Use reduced redundancy storage class
-s3-region string
    S3 region
-s3-secret-key string
    S3 secret key