A stateless Selenium hub for Kubernetes that creates a browser resource per session and proxies WebDriver traffic to it.
- Accepts standard WebDriver requests at
/wd/hub(and root). - Creates a
Browserresource viabrowser-servicebased on requested capabilities. - Waits for the browser pod to become ready, then proxies traffic to the sidecar
seleniferousinside that pod.
- Kubernetes cluster.
- browser-controller CRDs installed (for
BrowserandBrowserConfigresources). - browser-service running and reachable at
BROWSER_SERVICE_URL. - Browser pod image includes seleniferous sidecar listening on
PROXY_PORT.
Selenosis is configured via environment variables:
| Variable | Default | Description |
|---|---|---|
LISTEN_ADDR |
:4444 |
HTTP listen address. |
BROWSER_SERVICE_URL |
http://browser-service:8080 |
browser-service API base URL. |
PROXY_PORT |
4445 |
Sidecar port inside the browser pod. |
NAMESPACE |
default |
Kubernetes namespace where Browser resources are created. |
SESSION_CREATE_ATTEMPTS |
5 |
Reserved for retries (loaded but not currently used). |
SESSION_CREATE_TIMEOUT |
3m |
Reserved for timeouts (loaded but not currently used). |
BASIC_AUTH_FILE |
Points to a file containing a JSON list of users. |
Selenosis exposes Selenium-compatible endpoints on both / and /wd/hub.
| Method | Path | Description |
|---|---|---|
POST |
/session or /wd/hub/session |
Create a new WebDriver session. |
* |
/wd/hub/session/{sessionId}/* |
Proxy all session traffic (HTTP and WebSocket). |
GET |
/status or /wd/hub/status |
Simple service status response. |
* |
/selenosis/v1/sessions/{sessionId}/proxy/http/* |
Internal HTTP-only proxy used by Seleniferous. |
- Client calls
POST /wd/hub/sessionwith W3C capabilities. - Selenosis creates a
Browserresource viabrowser-service. - When the browser pod is
Running, Selenosis maps its IP to a UUID session id. - All session requests are proxied to the sidecar
seleniferousin that pod.
curl -sS -X POST http://{selenosis_host:port}/wd/hub/session \
-H 'Content-Type: application/json' \
-d '{
"capabilities": {
"alwaysMatch": {
"browserName": "chrome",
"browserVersion": "120.0"
}
}
}'The response is proxied from the browser and contains the sessionId used for subsequent requests.
curl -sS -X GET http://{selenosis_host:port}/wd/hub/session/<sessionId>/urlSelenosis supports WebDriver BiDi by proxying WebSocket connections per session.
To enable BiDi, request webSocketUrl: true in capabilities.
curl -X POST http://{selenosis_host:port}/wd/hub/session \
-H 'Content-Type: application/json' \
-d '{
"capabilities": {
"alwaysMatch": {
"browserName": "chrome",
"browserVersion": "120.0"
"webSocketUrl": true
}
}
}'The response will include capabilities.webSocketUrl for the BiDi connection.
Chromium-based browsers can expose Chrome DevTools Protocol (CDP).
Selenosis transparently proxies CDP traffic through the seleniferous sidecar.
If you run behind a reverse proxy or ingress, set these headers so Selenosis can build correct external URLs for the sidecar:
X-Forwarded-ProtoX-Forwarded-Host
Selenosis also adds Selenosis-Request-ID to outgoing requests for tracing.
selenosis:options is a vendor-namespaced WebDriver capability that allows users to pass session-specific overrides to Selenosis without changing CRDs or cluster-level configuration.
The options are attached to the Browser resource (via annotations) and applied by the controller at Pod creation time.
- labels: Kubernetes labels applied to the Browser Pod
- containers..env: environment variables injected into matching containers
The controller does not rely on hard-coded container names. It iterates over the Pod containers and applies overrides only when names match.
selenosis:options = {
"labels": { "<string>": "<string>" },
"containers": {
"<containerName>": {
"env": { "<ENV_NAME>": "<ENV_VALUE>" }
}
}
}labelsare optional.containersis optional.- Each section is applied independently.
{
"capabilities": {
"alwaysMatch": {
"browserName": "chrome",
"version": "139.0",
"selenosis:options": {
"labels": {
"team": "qa"
},
"containers": {
"browser": {
"env": {
"LOG_LEVEL": "debug"
}
},
"seleniferous": {
"env": {
"SESSION_IDLE_TIMEOUT": "3m"
}
}
}
}
}
}
}Result
Pod labels:
team=qa
Container browser receives:
LOG_LEVEL=debug
Container seleniferous receives:
SESSION_IDLE_TIMEOUT=3m
If a container name does not exist in the Pod, its configuration is ignored.
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class SelenosisOptionsExample {
public static void main(String[] args) throws Exception {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("browserName", "chrome");
// Global labels
Map<String, String> labels = new HashMap<>();
labels.put("project", "payments");
// Container env vars
Map<String, String> browserEnv = new HashMap<>();
browserEnv.put("LOG_LEVEL", "debug");
Map<String, Object> browserContainer = new HashMap<>();
browserContainer.put("env", browserEnv);
Map<String, Object> containers = new HashMap<>();
containers.put("browser", browserContainer);
Map<String, Object> selenosisOptions = new HashMap<>();
selenosisOptions.put("labels", labels);
selenosisOptions.put("containers", containers);
// Vendor-namespaced capability
caps.setCapability("selenosis:options", selenosisOptions);
RemoteWebDriver driver = new RemoteWebDriver(
new URL("http://{selenosis_host:port}/wd/hub"),
caps
);
try {
driver.get("https://example.com");
} finally {
driver.quit();
}
}
}selenosis:optionsis validated and parsed by the controller.- Invalid JSON results in the Browser being marked as Failed.
- Options are applied when the Pod is created.
Selenosis supports optional HTTP Basic Authentication for protecting its public API endpoints. Authentication is enabled by providing a users file via a Kubernetes Secret and referencing it through environment variables. When Basic Auth is enabled, all incoming HTTP requests must include valid credentials.
Basic Auth is disabled by default.
It becomes active when the following environment variable is set:
BASIC_AUTH_FILE
This variable must point to a file containing a JSON list of users.
[
{ "user": "alice", "pass": "secret1" },
{ "user": "bob", "pass": "secret2" }
]user— usernamepass— password (plain text in this example)
apiVersion: v1
kind: Secret
metadata:
name: basic-auth
namespace: selenosis
type: Opaque
stringData:
users.json: |
[
{ "user": "alice", "pass": "secret1" },
{ "user": "bob", "pass": "secret2" }
]
env:
- name: BASIC_AUTH_FILE
value: /etc/auth/users.json
volumeMounts:
- name: basic-auth
mountPath: /etc/auth
readOnly: true
volumes:
- name: basic-auth
secret:
secretName: basic-authClients must send credentials using http://{username}:{password}@{selenosis_host:port}/wd/hub
The project is built and packaged entirely via Docker. Local Go installation is not required for producing the final artifact.
The build process is controlled via the following Makefile variables:
Variable Description
- BINARY_NAME Name of the produced binary (selenosis).
- REGISTRY Docker registry prefix (default: localhost:5000).
- IMAGE_NAME Full image name (/selenosis).
- VERSION Image version/tag (default: develop).
- PLATFORM Target platform (default: linux/amd64).
- CONTAINER_TOOL docker cmd
REGISTRY, VERSION is expected to be provided externally, which allows the same Makefile to be used locally and in CI.
Minimal configuration
apiVersion: v1
kind: ServiceAccount
metadata:
name: selenosis
namespace: defaultapiVersion: v1
kind: Service
metadata:
name: selenosis
labels:
role: selenosis
spec:
type: NodePort
selector:
role: selenosis
ports:
- name: http
port: 4444
targetPort: 4444apiVersion: apps/v1
kind: Deployment
metadata:
name: selenosis
labels:
role: selenosis
spec:
replicas: 1
selector:
matchLabels:
role: selenosis
template:
metadata:
labels:
role: selenosis
spec:
serviceAccountName: selenosis
containers:
- name: service
image: alcounit/selenosis:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 4444
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"