BrowserGateway
Operating

Docker Deployment

Run browser-gateway in Docker for production deployments.

Run browser-gateway in Docker for production deployments.

Quick Start

docker run -d \
  --name browser-gateway \
  -p 9500:9500 \
  -v bg_data:/data \
  -v ./gateway.yml:/app/gateway.yml:ro \
  -e PROVIDER_TOKEN=your-token \
  -e BG_TOKEN=your-gateway-secret \
  ghcr.io/browser-gateway/server:latest

Open http://localhost:9500/web to access the dashboard.

Persistent Data

The gateway keeps its state in /data inside the container (BG_DATA_DIR):

  • saved profiles
  • the profile encryption key (/data/.encryption-key), unless you pass BG_ENCRYPTION_KEY
  • session recordings
  • gateway.yml, when you manage providers from the dashboard without mounting a config file

The image does not create a volume for you. Without a mount on /data, all of this is lost when the container is removed or recreated, including on every image update. Losing the encryption key makes every saved profile unreadable. Always mount a named volume or a host directory on /data, as the examples on this page do.

Active sessions, parked sessions, the request queue and provider health are kept in memory and reset on restart.

Docker Compose

services:
  browser-gateway:
    image: ghcr.io/browser-gateway/server:latest
    ports:
      - "9500:9500"
    volumes:
      - bg_data:/data
      - ./gateway.yml:/app/gateway.yml:ro
    environment:
      - PROVIDER_TOKEN=${PROVIDER_TOKEN}
      - BG_TOKEN=${BG_TOKEN}
    restart: unless-stopped

volumes:
  bg_data:

See docker-compose.yml at the root of the repo for a ready-to-use template with the same volume and a health check.

No Config File

The gateway starts without a config file. It creates /data/gateway.yml on first boot, and providers you add through the dashboard at /web/providers are saved there.

docker run -d \
  --name browser-gateway \
  -p 9500:9500 \
  -v bg_data:/data \
  -e BG_TOKEN=your-gateway-secret \
  ghcr.io/browser-gateway/server:latest

Configuration

Mount a config file

-v ./gateway.yml:/app/gateway.yml:ro

A mounted /app/gateway.yml is used only when /data/gateway.yml does not exist, because the gateway checks the data directory first. To be explicit, set BG_CONFIG_PATH=/app/gateway.yml. A read-only mount (:ro) also means dashboard edits to providers and webhooks cannot be saved; drop :ro if you want to edit from the dashboard.

Secrets in the config use ${ENV_VAR} references. Pass the actual values as environment variables:

-e PROVIDER_TOKEN=xxx -e BACKUP_KEY=yyy

Environment variables

VariableDescription
BG_TOKENAuth token for gateway access
BG_ENCRYPTION_KEYProfile encryption key. Auto-generated and persisted at /data/.encryption-key if unset. Override for centralized key management (Vault, AWS Secrets Manager).
BG_DATA_DIRWhere the gateway stores config, profiles, recordings and the encryption key. Default /data.
BG_CONFIG_PATHPath to gateway.yml. Default $BG_DATA_DIR/gateway.yml.
BG_ALLOWED_ORIGINSComma-separated CORS allowlist. Default: same-origin only.
BG_PUBLIC_URLPublic base URL the gateway gives to agents, e.g. https://gateway.example.com. Set this when the container sits behind a domain or proxy.
BG_TRUST_PROXYSet to 1 behind a reverse proxy that overwrites X-Forwarded-Host.
BG_ALLOWED_HOSTSComma-separated list of extra Host header values accepted on /mcp when BG_TOKEN is unset. Default: loopback only.
PORTServer port. Default 9500. 12-factor convention used by Railway, Render, Fly, Heroku.
HOSTBind interface. The image sets this to 0.0.0.0, so a container listens on every interface whether or not BG_TOKEN is set.
LOG_LEVELdebug / info / warn / error. Overrides gateway.yml.
HTTP_PROXY, HTTPS_PROXY, NO_PROXYOutbound proxy. Honored by Node's built-in fetch.
TZTimezone for log timestamps.

Networking

The gateway needs to reach your browser providers.

Providers on the same Docker network

services:
  browser-gateway:
    image: ghcr.io/browser-gateway/server:latest
    ports: ["9500:9500"]
    volumes:
      - bg_data:/data
      - ./gateway.yml:/app/gateway.yml:ro

  playwright:
    image: mcr.microsoft.com/playwright:v1.50.1-noble
    command: npx -y playwright@1.50.1 run-server --port 3000 --host 0.0.0.0
    shm_size: '1gb'

volumes:
  bg_data:

Config:

providers:
  playwright:
    url: ws://playwright:3000

Providers on the host machine

Use host.docker.internal (Docker Desktop) or --network host (Linux):

providers:
  local-chrome:
    url: http://host.docker.internal:9222

External providers (cloud)

No special networking needed:

providers:
  cloud-provider:
    url: wss://provider.example.com?token=${PROVIDER_TOKEN}

Health Check

The Docker image has a built-in health check that polls /health every 30 seconds.

docker inspect browser-gateway --format='{{.State.Health.Status}}'

Updating

docker compose pull
docker compose up -d

The gateway starts immediately. No migrations needed. Profiles, recordings and the encryption key carry over as long as /data is on a volume.

Image Details

PropertyValue
Registryghcr.io/browser-gateway/server
Base imagenode:22-slim
Size~370MB
UserNon-root (bguser, uid 1001). The entrypoint starts as root only to take ownership of /data, then drops privileges.
Port9500
Data directory/data (mount a volume here)
Health checkBuilt-in (30s interval)

On this page