Deploy Browserless on Kubernetes with Browserless v2 defaults, token authentication enforcement, and image presets for multiple browsers.
CONCURRENT, QUEUED, TIMEOUT, etc.)config.token, config.tokenSecretRef, or extraEnv with TOKEN)image.browser (chromium, chrome, firefox, multi)helm repo add icoretech https://icoretech.github.io/helm
helm repo update
helm upgrade --install browserless icoretech/browserless \
-n browserless --create-namespace \
--set config.token="replace-me"
OCI:
helm upgrade --install browserless oci://ghcr.io/icoretech/charts/browserless \
-n browserless --create-namespace \
--set config.token="replace-me"
config:
token: null
tokenSecretRef:
name: browserless-auth
key: token
image:
browser: firefox
# repository: ghcr.io/browserless/firefox # optional explicit override
# tag: v2.57.0 # optional override
Set config.strictTokenUse=true to require the configured token on HTTP and WebSocket routes that normally opt out of authentication, including direct page connections. Static files remain public so debugger and DevTools assets can load. Leaving this field unset preserves upstream’s default (false).
config:
token: null
tokenSecretRef:
name: browserless-auth
key: token
strictTokenUse: true
The default readiness probe and Helm test already use the authenticated /pressure endpoint and work with strict mode. Custom probes, management clients, and direct page WebSocket clients must also supply a valid token. Strict mode requires a token at application startup; the chart’s existing token validation remains in effect.
Each browser session gets an owned directory under config.scratchDir, passed to the browser as TMPDIR and removed when the session ends. Unset config.scratchDir uses browserless-scratch-dirs under the container’s temporary directory.
Use a writable, pod-local emptyDir for a dedicated scratch mount. Size it for your session concurrency and workload; this example limits it to 1 GiB and grants the published image’s group (999) write access. Keep scratch separate from reusable browser profiles in config.dataDir, and use short paths to stay within Chromium’s Unix socket path-length limit.
config:
scratchDir: /scratch/browserless
podSecurityContext:
fsGroup: 999
volumes:
- name: browser-scratch
emptyDir:
sizeLimit: 1Gi
volumeMounts:
- name: browser-scratch
mountPath: /scratch
Active browsers and session/reconnect identifiers live inside the replica that created them. An established WebSocket stays on its replica, but reconnect and session-management requests must reach that same replica through affinity or explicit routing. A shared volume does not share running browser processes. Account for active sessions before scaling down or rolling out pods, and keep scratch storage local to each pod.
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: icoretech
namespace: flux-system
spec:
type: oci
interval: 30m
url: oci://ghcr.io/icoretech/charts
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: browserless
namespace: browserless
spec:
interval: 5m
chart:
spec:
chart: browserless
version: ">=0.1.1"
sourceRef:
kind: HelmRepository
name: icoretech
namespace: flux-system
values:
image:
browser: chromium
config:
tokenSecretRef:
name: browserless-auth
key: token
| Key | Type | Default | Description |
|---|---|---|---|
| affinity | object | {} |
Affinity. |
| autoscaling.enabled | bool | false |
Enable HPA. |
| autoscaling.maxReplicas | int | 20 |
Maximum replicas. |
| autoscaling.minReplicas | int | 1 |
Minimum replicas. |
| autoscaling.targetCPUUtilizationPercentage | int | 80 |
Target CPU utilization percentage. |
| autoscaling.targetMemoryUtilizationPercentage | string | nil |
Target memory utilization percentage. |
| config | object | {"allowFileProtocol":null,"allowGet":null,"concurrent":4,"corsAllowMethods":null,"corsAllowOrigin":null,"corsMaxAge":null,"cpuEmaAlpha":null,"cpuOverloadHysteresis":null,"cpuSampleIntervalMs":null,"dataDir":null,"debug":"-*","downloadDir":null,"enableCors":null,"errorAlertUrl":null,"external":null,"host":"0.0.0.0","machineStatsSource":null,"maxCpuPercent":null,"maxMemoryPercent":null,"metricsJsonPath":null,"port":3000,"queued":10,"scratchDir":null,"strictTokenUse":null,"timeout":600000,"token":"change-me","tokenSecretRef":null} |
Browserless configuration mapped to environment variables. |
| config.allowFileProtocol | string | nil |
Allow file:// protocol. |
| config.allowGet | string | nil |
Allow GET requests. |
| config.concurrent | int | 4 |
Max concurrent sessions. |
| config.corsAllowMethods | string | nil |
CORS allowed methods. |
| config.corsAllowOrigin | string | nil |
CORS allowed origins. |
| config.corsMaxAge | string | nil |
CORS max age (seconds). |
| config.cpuEmaAlpha | string | nil |
CPU exponential moving average alpha for overload smoothing. |
| config.cpuOverloadHysteresis | string | nil |
CPU overload hysteresis in percentage points. |
| config.cpuSampleIntervalMs | string | nil |
CPU sampler interval in milliseconds for overload smoothing. |
| config.dataDir | string | nil |
Data directory path. |
| config.debug | string | "-*" |
Debug selector (e.g. “-*” to reduce logs). |
| config.downloadDir | string | nil |
Download directory path. |
| config.enableCors | string | nil |
Enable CORS. |
| config.errorAlertUrl | string | nil |
Callback URL for Browserless runtime errors. |
| config.external | string | nil |
Example: https://browserless.example.com |
| config.host | string | "0.0.0.0" |
Bind host. |
| config.machineStatsSource | string | nil |
Machine stats source for overload checks (auto, host, or cgroup). |
| config.maxCpuPercent | string | nil |
Overload protection CPU threshold percentage. |
| config.maxMemoryPercent | string | nil |
Overload protection memory threshold percentage. |
| config.metricsJsonPath | string | nil |
Metrics JSON path. |
| config.port | int | 3000 |
Bind port. |
| config.queued | int | 10 |
Max queued sessions. |
| config.scratchDir | string | nil |
Root for Browserless-owned per-session temporary files. Null uses the upstream temporary directory; keep this separate from reusable browser profiles. |
| config.strictTokenUse | bool | nil |
Require TOKEN on all routes, including routes that normally opt out of authentication. Static files remain public. Null preserves the upstream default (false). |
| config.timeout | int | 600000 |
Max session timeout in milliseconds. |
| config.token | string | "change-me" |
Change this in production. |
| config.tokenSecretRef | string | nil |
key: token |
| envFrom | list | [] |
Extra envFrom entries. |
| extraEnv | list | [] |
Additional environment variables. |
| fullnameOverride | string | "" |
Override fully-qualified release name. |
| image.browser | string | "chromium" |
Supported presets: chromium, chrome, firefox, multi. |
| image.pullPolicy | string | "IfNotPresent" |
Image pull policy. |
| image.repository | string | "" |
Image repository override. When empty, defaults to ghcr.io/browserless/ |
| image.tag | string | "" |
Image tag override. Defaults to Chart appVersion. |
| imagePullSecrets | list | [] |
List of image pull secrets. |
| livenessProbe | object | {"failureThreshold":3,"initialDelaySeconds":5,"periodSeconds":10,"successThreshold":1,"tcpSocket":{"port":"http"},"timeoutSeconds":1} |
Liveness probe. |
| nameOverride | string | "" |
Override chart name. |
| nodeSelector | object | {} |
Node selector. |
| podAnnotations | object | {} |
Pod annotations. |
| podLabels | object | {} |
Pod labels. |
| podSecurityContext | object | {} |
Pod security context. |
| readinessProbe | object | {"exec":{"command":["/bin/sh","-ec","wget -qO- \"http://127.0.0.1:${PORT:-3000}/pressure?token=${TOKEN}\" >/dev/null"]},"failureThreshold":3,"initialDelaySeconds":5,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1} |
Readiness probe. |
| replicaCount | int | 1 |
Number of replicas. |
| resources | object | {} |
Container resources. |
| securityContext | object | {} |
Container security context. |
| service.annotations | object | {} |
Service annotations. |
| service.externalTrafficPolicy | string | nil |
External traffic policy. |
| service.loadBalancerIP | string | nil |
Optional LoadBalancer IP. |
| service.loadBalancerSourceRanges | list | [] |
Optional CIDRs allowed via LoadBalancer. |
| service.nodePort | string | nil |
Optional nodePort. |
| service.port | int | 80 |
Service port. |
| service.type | string | "ClusterIP" |
Service type. |
| serviceAccount.annotations | object | {} |
Service account annotations. |
| serviceAccount.create | bool | true |
Create a service account. |
| serviceAccount.name | string | "" |
Service account name. |
| tolerations | list | [] |
Tolerations. |
| volumeMounts | list | [] |
Additional volume mounts. |
| volumes | list | [] |
Additional volumes. |