Skip to main content

Thunders Browserless Self-Hosting Guide

This guide covers deploying the Thunders Browserless image on your own Azure infrastructure, enabling secure browser automation through Azure Relay.

Written by Ines
Updated over a month ago

Overview

The Thunders Browserless image is a unified container that packages:

  • Browserless Enterprise - Headless browser engine supporting Chromium, Firefox, and WebKit

  • Nginx Reverse Proxy - Request routing, WebSocket proxying, health checks, and file serving

  • Azure Relay Bridge - Outbound-only connection to Azure Relay for secure communication with Thunders's platform

All three components run inside a single container managed by supervisord, making deployment straightforward.

How It Works

Key points:

  • No inbound firewall rules required - The relay bridge connects outbound to Azure Relay over HTTPS (port 443)

  • No VPN needed - Azure Relay handles secure tunneling

  • Single container - One image to pull, one container to run


Prerequisites

  • An Azure subscription (or any environment that can run Docker containers)

  • ACR credentials provided by Thunders (to pull the image)

  • Azure Relay configuration (see Azure Relay section below)

  • For Azure deployments: Azure CLI installed and authenticated


Credentials You Will Receive from Thunders

Thunders will provide the following credentials for your deployment:

Credential

Description

ACR Server

Container registry URL (e.g., youracr.azurecr.io)

ACR Username

Token name for pulling the image

ACR Password

Token password for pulling the image

Relay Namespace

Azure Relay namespace (if Thunders-managed)

Relay Hybrid Connection Name

Hybrid connection name (if Thunders-managed)

Relay Listen Key

SAS key for the relay listener (if Thunders-managed)

Store these securely. Never commit them to source control.


Azure Relay Configuration

Azure Relay enables secure communication between Thunders's platform and your self-hosted browserless instance without opening inbound firewall ports. There are two setup options:

Option A: Thunders-Managed Relay (Recommended)

Thunders creates and manages the Azure Relay namespace and hybrid connection in Thunders's Azure subscription. You will receive:

  • RELAY_NAMESPACE - The relay namespace URL

  • RELAY_HYCO - The hybrid connection name

  • RELAY_LISTEN_KEY - The SAS key with listen permission

Your container connects outbound to this relay. Thunders's platform connects from the other side using send permissions.

No Azure Relay setup required on your end.

Option B: Customer-Managed Relay

If you prefer to manage the relay in your own Azure subscription:

  1. Create an Azure Relay namespace:

az relay namespace create \  
--resource-group \
--name \
--location

2. Create a hybrid connection:

az relay hyco create \  
--resource-group \
--namespace-name \
--name

3. Create authorization rules:

Create a listen rule (for your self-hosted container):

az relay hyco authorization-rule create \   
--resource-group \
--namespace-name \
--hybrid-connection-name \
--name listen \
--rights Listen

Create a send rule (for Thunders's platform):

az relay hyco authorization-rule create \  
--resource-group \
--namespace-name \
--hybrid-connection-name \
--name send \
--rights Send

4. Retrieve the keys:

Get the listen key (for your container):

az relay hyco authorization-rule keys list \   
--resource-group \
--namespace-name \
--hybrid-connection-name \
--name listen \
--query primaryKey -o tsv

Get the send key (share this with Thunders):

az relay hyco authorization-rule keys list \ 
--resource-group \
--namespace-name \
--hybrid-connection-name \
--name send \
--query primaryKey -o tsv

5. Share with Thunders:

  • Relay namespace: .servicebus.windows.net

  • Hybrid connection name:

  • Send key name: send

  • Send key secret: (the send key from step 4)

Thunders will configure these in their platform to connect to your relay.


Environment Variables Reference

Required Variables

Variable

Description

Example

RELAY_NAMESPACE

Azure Relay namespace

myrelay.servicebus.windows.net

RELAY_HYCO

Hybrid connection name

thunder-browserless

RELAY_LISTEN_KEY

SAS key with listen permission

base64-encoded-key

Optional Variables

Variable

Default

Description

CONCURRENT

15

Maximum concurrent browser sessions

TIMEOUT

1800000

Session timeout in milliseconds (default: 30 minutes)

EXTERNAL

External URL of this instance (used in health responses)

Ports

Port

Service

Description

3000

Nginx (exposed)

Main entry point, health checks, WebSocket proxy

3001

Browserless (internal)

Browser engine (proxied through Nginx)

3002

Health check (internal)

Health score calculation service

8080

Relay bridge (internal)

Azure Relay bridge health endpoint

Only port 3000 needs to be exposed.


Deployment Options

Option 1: Azure Container Apps (Recommended)

Azure Container Apps provides a managed container hosting platform with built-in scaling, health probes, and HTTPS.

Quick Deploy with Azure CLI

# Login to Azure 
az login

# Set variables
RESOURCE_GROUP="rg-thunder-browserless"
LOCATION="westeurope"
ENV_NAME="env-thunder-browserless"
APP_NAME="thunder-browserless"

# Create resource group
az group create
--name $RESOURCE_GROUP
--location $LOCATION

# Create Container Apps environment
az containerapp env create \
--name $ENV_NAME \
--resource-group $RESOURCE_GROUP \
--location $LOCATION

# Deploy the container app
az containerapp create \
--name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--environment $ENV_NAME \
--image <THUNDER_ACR_SERVER>/thunder-browserless:latest \
--registry-server <THUNDER_ACR_SERVER> \
--registry-username <THUNDER_ACR_USERNAME> \
--registry-password <THUNDER_ACR_PASSWORD> \
--target-port 3000 \
--ingress internal \
--cpu 4.0 \
--memory 8.0Gi \
--min-replicas 1 \
--max-replicas 1 \
--env-vars \
CONCURRENT=15 \
TIMEOUT=1800000 \
RELAY_NAMESPACE=<YOUR_RELAY_NAMESPACE> \
RELAY_HYCO=<YOUR_RELAY_HYCO> \
RELAY_LISTEN_KEY=secretref:relay-key \
--secrets \
relay-key=<YOUR_RELAY_LISTEN_KEY>

Deploy with ARM Template

Use the provided ARM template for a more configurable deployment:

az deployment group create \
--resource-group $RESOURCE_GROUP \
--template-file customer-browserless-template.json \
--parameters \
containerAppName=$APP_NAME \
containerAppEnvironmentId=$(az containerapp env show -g $RESOURCE_GROUP -n $ENV_NAME --query id -o tsv) \
containerImage=<THUNDER_ACR_SERVER>/thunder-browserless:latest \
acrServer=<THUNDER_ACR_SERVER> \
acrUsername=<THUNDER_ACR_USERNAME> \
acrPassword=<THUNDER_ACR_PASSWORD> \
relayNamespace=<YOUR_RELAY_NAMESPACE> \
relayHycoName=<YOUR_RELAY_HYCO> \
relayListenKey=<YOUR_RELAY_LISTEN_KEY>

Option 2: Azure Container Instances (ACI)

Azure Container Instances provides serverless container hosting. Simpler but with fewer features than Container Apps.

# Create resource group 
az group create --name rg-thunder-browserless --location westeurope

# Deploy container az container create \
--resource-group rg-thunder-browserless \
--name thunder-browserless \
--image <THUNDER_ACR_SERVER>/thunder-browserless:latest \
--registry-login-server <THUNDER_ACR_SERVER> \
--registry-username <THUNDER_ACR_USERNAME> \
--registry-password <THUNDER_ACR_PASSWORD> \
--cpu 4 \
--memory 8 \
--ports 3000 \
--ip-address Private \
--environment-variables \
CONCURRENT=15 \
TIMEOUT=1800000 \
RELAY_NAMESPACE=<YOUR_RELAY_NAMESPACE> \
RELAY_HYCO=<YOUR_RELAY_HYCO> \
RELAY_LISTEN_KEY=<YOUR_RELAY_LISTEN_KEY> \
--restart-policy Always

# Check status az container show \
--resource-group rg-thunder-browserless \
--name thunder-browserless \
--query "{status:instanceView.state, ip:ipAddress.ip}" \
-o table

Option 3: Azure Kubernetes Service (AKS)

For organizations already running AKS clusters.

Create Kubernetes Secret

# Create namespace 
kubectl create namespace thunder

# Create image pull secret
kubectl create secret docker-registry thunder-acr \
--namespace thunder \
--docker-server=<THUNDER_ACR_SERVER> \
--docker-username=<THUNDER_ACR_USERNAME> \
--docker-password=<THUNDER_ACR_PASSWORD>

# Create secrets for environment variables
kubectl create secret generic thunder-browserless-secrets \
--namespace thunder \
--from-literal=RELAY_LISTEN_KEY=<YOUR_RELAY_LISTEN_KEY>

Kubernetes Deployment Manifest

Save the following as thunder-browserless.yaml:

apiVersion: apps/v1 
kind: Deployment
metadata:
name: thunder-browserless
namespace: thunder
spec: replicas: 1
selector:
matchLabels:
app: thunder-browserless
template:
metadata:
labels:
app: thunder-browserless
spec:
imagePullSecrets:
- name: thunder-acr
containers:
- name: thunder-browserless
image: <THUNDER_ACR_SERVER>/thunder-browserless:latest
ports:
- containerPort: 3000
env:
- name: CONCURRENT
value: "15"
- name: TIMEOUT
value: "1800000"
- name: RELAY_NAMESPACE
value: "<YOUR_RELAY_NAMESPACE>"
- name: RELAY_HYCO
value: "<YOUR_RELAY_HYCO>"
- name: RELAY_LISTEN_KEY
valueFrom:
secretKeyRef:
name: thunder-browserless-secrets
key: RELAY_LISTEN_KEY
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
startupProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 10
failureThreshold: 30
livenessProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 10
timeoutSeconds: 10
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: thunder-browserless
namespace: thunder
spec:
type: ClusterIP
selector:
app: thunder-browserless
ports:
- port: 3000
targetPort: 3000
protocol: TCP

Apply:

kubectl apply -f thunder-browserless.yaml

Option 4: Docker (VM or On-Premises)

For running directly on a VM or on-premises server.

Pull and Run

# Login to Thunders's ACR
docker login <THUNDER_ACR_SERVER> \
-u <THUNDER_ACR_USERNAME> \
-p <THUNDER_ACR_PASSWORD>

# Pull the image
docker pull <THUNDER_ACR_SERVER>/thunder-browserless:latest

# Run the container
docker run -d \
--name thunder-browserless \
--restart unless-stopped \
-p 3000:3000 \
-e CONCURRENT=15 \
-e TIMEOUT=1800000 \
-e RELAY_NAMESPACE=<YOUR_RELAY_NAMESPACE> \
-e RELAY_HYCO=<YOUR_RELAY_HYCO> \
-e RELAY_LISTEN_KEY=<YOUR_RELAY_LISTEN_KEY> \
--shm-size=2g \
<THUNDER_ACR_SERVER>/thunder-browserless:latest

Important: The --shm-size=2g flag is required. Chromium uses /dev/shm for shared memory, and the default 64MB is insufficient for browser automation.

Docker Compose

Save as docker-compose.yml:

services:
thunder-browserless:
image: <THUNDER_ACR_SERVER>/thunder-browserless:latest
container_name: thunder-browserless
restart: unless-stopped
ports:
- "3000:3000"
environment:
- CONCURRENT=${CONCURRENT:-15}
- TIMEOUT=${TIMEOUT:-1800000}
- RELAY_NAMESPACE=${RELAY_NAMESPACE}
- RELAY_HYCO=${RELAY_HYCO}
- RELAY_LISTEN_KEY=${RELAY_LISTEN_KEY}
shm_size: 2g
deploy:
resources:
limits:
cpus: "4.0"
memory: 8G
reservations:
cpus: "2.0"
memory: 4G

Create a .env file alongside docker-compose.yml:

RELAY_NAMESPACE=your-relay.servicebus.windows.net
RELAY_HYCO=your-hybrid-connection
RELAY_LISTEN_KEY=your-listen-key
CONCURRENT=15
TIMEOUT=1800000

Run:

docker compose up -d

Health Checks and Monitoring

Health Endpoint

The container exposes health checks at:

  • GET /health - Returns health status with score

  • GET /healthz - Alias for /health

Healthy response (HTTP 200):

{
"origin": "http://localhost:3000",
"healthy": true,
"score": 85,
"error": null,
"pressure": {
"cpu": 15,
"memory": 45,
"running": 2,
"maxConcurrent": 15,
"queued": 0,
"isAvailable": true
},
"scores": {
"cpu": 84,
"memory": 44,
"concurrent": 87
},
"timestamp": "2026-01-15T10:30:00.000Z"
}

Unhealthy response (HTTP 503):

Returned when CPU exceeds 95%, memory exceeds 80%, or concurrent sessions are at maximum.

Monitoring Recommendations

  • Poll /health every 15-30 seconds

  • Alert when healthy is false for more than 2 consecutive checks

  • Monitor pressure.running vs pressure.maxConcurrent for capacity planning

  • Track scores.cpu and scores.memory trends for scaling decisions

Viewing Logs

Azure Container Apps:

az containerapp logs show -g <resource-group> -n <app-name> --follow

Azure Container Instances:

az container logs --resource-group <resource-group> --name <container-name> --follow

Docker:

docker logs -f thunder-browserless

Kubernetes:

kubectl logs -f deployment/thunder-browserless -n thunder

Troubleshooting

Container fails to start

Check logs for missing environment variables:

Required environment variable RELAY_NAMESPACE is not set

Ensure all required environment variables are set.

Verify ACR credentials:

echo '<password>' | docker login <THUNDER_ACR_SERVER> -u <username> --password-stdin
docker pull <THUNDER_ACR_SERVER>/thunder-browserless:latest

Relay bridge not connecting

  1. Verify outbound connectivity on port 443 to *.servicebus.windows.net

  2. Check relay bridge logs for connection errors

  3. Verify RELAY_NAMESPACE, RELAY_HYCO, and RELAY_LISTEN_KEY are correct

  4. The relay bridge auto-reconnects every 5 seconds on disconnect

Health check returns 503

  • CPU threshold exceeded: Reduce CONCURRENT or allocate more CPU

  • Memory threshold exceeded: Allocate more memory or reduce CONCURRENT

  • All sessions busy: Wait for sessions to complete or increase CONCURRENT

Browser sessions timing out

  • Increase TIMEOUT (default: 1800000ms = 30 minutes)

  • Check network latency between your infrastructure and Azure Relay

Shared memory errors

If running with Docker, ensure --shm-size=2g is set. Chromium requires more than the default 64MB of shared memory.


Resource Sizing Guide

Concurrent Sessions

CPU

Memory

Shared Memory

5

2 cores

4 GB

1 GB

15 (default)

4 cores

8 GB

2 GB

30

8 cores

16 GB

4 GB

60

16 cores

32 GB

8 GB

These are recommended minimums. Actual requirements depend on the complexity of the web pages being tested.

Network Requirements

Direction

Protocol

Port

Destination

Purpose

Outbound

HTTPS/WSS

443

*.servicebus.windows.net

Azure Relay connection

Outbound

HTTPS

443

Target web applications

Browser automation

Inbound (optional)

HTTP

3000

Local network

Health checks (not required for relay)

No inbound internet access is required for the relay connection to work.

For technical assistance or questions about architecture, please reach out to our support team at [email protected].

Other related reads you might find useful:

Did this answer your question?