NGINX Load Balancing
For production deployments, you'll often want to use NGINX as a reverse proxy and load balancer for Browserless Docker containers. It provides essential benefits including SSL termination, traffic distribution across multiple instances, and enhanced reliability through health monitoring and automatic failover.
How NGINX Works as a Load Balancer
NGINX acts as a reverse proxy that sits between your clients and Browserless instances. When a request arrives, NGINX:
- Receives the incoming connection - Accepts HTTP/HTTPS requests and WebSocket connections from clients
- Routes to a backend - Selects one of the healthy Browserless instances based on the configured load balancing algorithm (e.g., least connections, round-robin)
- Forwards the request - Proxies the request to the selected Browserless container, maintaining WebSocket connections for browser automation
- Handles failures - If a backend instance fails or returns an error, automatically retries the request on another healthy instance
- Returns the response - Sends the backend's response back to the client
This architecture allows you to scale horizontally by adding more Browserless containers without changing your client code, while NGINX ensures traffic is distributed efficiently and failures are handled gracefully.
Why Use NGINX with Browserless?
- Load Balancing: Distribute traffic across multiple browserless containers
- SSL Termination: Handle HTTPS certificates at the proxy level
- Health Checks: Automatically retry failed requests to healthy instances
- WebSocket Support: Properly handle WebSocket connections for Puppeteer and Playwright
- High Availability: Prevent single points of failure
Docker Compose Setup
Here's a complete example using Docker Compose with NGINX load balancing two browserless instances:
docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
ports:
- "80:80"
depends_on:
- browserless_one
- browserless_two
restart: unless-stopped
browserless_one:
image: registry.browserless.io/browserless/browserless/enterprise:latest
environment:
- TOKEN=your-token-here
- CONCURRENT=10
restart: unless-stopped
browserless_two:
image: registry.browserless.io/browserless/browserless/enterprise:latest
environment:
- TOKEN=your-token-here
- CONCURRENT=10
restart: unless-stopped
nginx.conf
upstream browserless {
least_conn;
server browserless_one:3000;
server browserless_two:3000;
}
server {
listen 80;
location / {
proxy_pass http://browserless;
proxy_next_upstream error timeout http_500 http_503 http_429 non_idempotent;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# Extended timeouts for long-running browser operations
proxy_connect_timeout 900;
proxy_send_timeout 900;
proxy_read_timeout 900;
send_timeout 900;
}
}
Configuration Explanation
Upstream Block
The upstream block defines the backend browserless instances:
least_conn: Routes requests to the server with the fewest active connectionsserver browserless_one:3000: References the Docker service name and internal port- Multiple servers provide redundancy and load distribution
Server and Location Blocks
The server and location blocks handle incoming requests:
listen 80: Listens for HTTP traffic on port 80proxy_pass http://browserless: Routes requests to the upstream backend poolproxy_next_upstream: Automatically retries failed requests (errors, timeouts, 500/503/429 responses) on other serversproxy_http_version 1.1: Required for WebSocket supportUpgradeandConnectionheaders: Essential for WebSocket connections used by Puppeteer and Playwright- Extended timeouts (900 seconds): Accommodate long-running browser operations like scraping or PDF generation
Starting the Setup
- Create the
nginx.conffile with the configuration above - Create the
docker-compose.ymlfile - Start the services:
docker-compose up
Your browserless instances will be available at http://localhost with automatic load balancing.
Advanced Configuration
Health Checks
Enable health checks in Browserless to make load balancing more intelligent. When CPU or memory usage is high, Browserless will reject requests, causing NGINX to automatically route traffic to healthier instances:
# docker-compose.yml
browserless_one:
image: registry.browserless.io/browserless/browserless/enterprise:latest
environment:
- TOKEN=your-token-here
- CONCURRENT=10
- HEALTH=true
browserless_two:
image: registry.browserless.io/browserless/browserless/enterprise:latest
environment:
- TOKEN=your-token-here
- CONCURRENT=10
- HEALTH=true
SSL/HTTPS Configuration
For production deployments, add SSL support. Update your nginx.conf to listen on port 443 and include SSL certificates:
upstream browserless {
least_conn;
server browserless_one:3000;
server browserless_two:3000;
}
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /path/to/your/certificate.pem;
ssl_certificate_key /path/to/your/private-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://browserless;
proxy_next_upstream error timeout http_500 http_503 http_429 non_idempotent;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Extended timeouts
proxy_connect_timeout 900;
proxy_send_timeout 900;
proxy_read_timeout 900;
send_timeout 900;
}
}
# Optional: Redirect HTTP to HTTPS
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
Also update your docker-compose.yml to expose port 443:
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- /path/to/certificates:/etc/nginx/certs:ro
ports:
- "80:80"
- "443:443"
depends_on:
- browserless_one
- browserless_two
restart: unless-stopped
Browserless Proxy Awareness
When using NGINX in front of Browserless, configure Browserless to be aware of the proxy for proper link generation:
# docker-compose.yml
browserless_one:
image: registry.browserless.io/browserless/browserless/enterprise:latest
environment:
- TOKEN=your-token-here
- CONCURRENT=10
- EXTERNAL=https://your-domain.com
The EXTERNAL variable should be set to the fully-qualified URL (including protocol and port if non-standard) where your Browserless instances are accessible externally. See the Docker Configuration documentation for more details.
Scaling to More Instances
To add more Browserless instances, simply add more services to your docker-compose.yml and update the nginx upstream block:
# docker-compose.yml
services:
nginx:
# ... nginx config
depends_on:
- browserless_one
- browserless_two
- browserless_three
- browserless_four
browserless_one:
# ... config
browserless_two:
# ... config
browserless_three:
# ... config
browserless_four:
# ... config
# nginx.conf
upstream browserless {
least_conn;
server browserless_one:3000;
server browserless_two:3000;
server browserless_three:3000;
server browserless_four:3000;
}
Testing Your Setup
You can test your load-balanced setup by connecting with Puppeteer:
const puppeteer = require('puppeteer');
const browser = await puppeteer.connect({
browserWSEndpoint: 'ws://localhost:80?token=your-token-here'
});
const page = await browser.newPage();
await page.goto('https://example.com');
const title = await page.title();
console.log(title);
await browser.close();
The requests will be automatically distributed across your browserless instances by NGINX.