TL;DR
- Self-hosting Browserless involves running the Browserless container – the same browser automation stack behind the cloud – on infrastructure you own, under an Enterprise licence or the open-source SSPL build.
- A working deployment is three commands: authenticate to the registry, pull a pinned image, and run it with
KEY,TOKEN, and--shm-size=2g. - Production needs Docker Compose or Kubernetes,
EXTERNALset to your public URL, and health-aware load balancing across containers. - Self-hosting suits data-sovereignty, air-gapped, and protected health information (PHI) workloads – Browserless never sees your session data.
Run production browser automation on infrastructure you control, with the same Puppeteer, Playwright, and REST APIs you already use in the cloud.
This guide walks through the full deployment: first container, production configuration, high availability, GPU, Kubernetes, and the compliance questions that come up in security reviews.
Some workloads can’t leave your network. You might be handling protected health information, running inside an air-gapped environment, or working under network policies that a managed service can’t satisfy. Self-hosting the Enterprise Docker image gives you the full Browserless stack on hardware you own, with no usage-based billing and no data leaving your perimeter.
You keep the developer experience of the cloud and gain control over where everything runs.
Why teams self-host
Running your own containers means you own the full stack, from the network policy down to the storage volume. That control is the reason most teams move off shared infrastructure.
- Data sovereignty – Your session data, cookies, and downloads stay on infrastructure you control, which keeps you inside your own compliance boundary.
- Air-gapped deployment – Once you’ve pulled the image, the Enterprise build runs without an outbound dependency for licensing or usage tracking, so it works in networks with no internet access. Licence keys are time-limited, so agree a renewal process with our team before you cut off egress.
- Custom networking – Apply your own firewall rules, private DNS, and egress policies without waiting on a vendor.
- Predictable cost – Enterprise runs on a license, not usage-based billing, so your cost doesn't scale with session count or units as volume grows.
- Same APIs everywhere – Your Puppeteer and Playwright code connects over the same WebSocket endpoints as the cloud, and the REST and BrowserQL paths are identical, so your automation moves over unchanged.
What you get in the Enterprise image
The Enterprise image is the same container we run for Private Deployment customers, licensed to run on your own machines, with the same APIs as our cloud. It bundles Chrome, Chromium, Firefox, WebKit, and Edge in a single container, alongside the features production workloads rely on.
- Advanced session management with queueing, priority handling, and session persistence.
- Built-in monitoring through health checks, a
/pressureendpoint, and a/metricsexport – all also in the open-source image. Enterprise adds full lifecycle webhooks and OpenTelemetry, so traces, metrics, and logs go to any OTLP backend instead of a hosted dashboard. - Live debugging and session recording for inspecting automation as it runs.
- Direct engineering support for deployment and scaling questions.
- BrowserQL and stealth – the BQL endpoint, stealth mode, and CAPTCHA solving – aren't included in the open-source image.
If you want to evaluate the core automation features first, the open-source image is free under SSPL-1.0. See open source or Enterprise below for where the licence boundary sits.
Before you start
You’ll need a few things in place before your first container runs:
- Docker 20.10 or later, Podman, or Kubernetes.
- An Enterprise license key, available from our team.
- Registry credentials for the private registry, also from our team.
- A host with at least 2 CPU cores and 4GB of RAM, which is the floor for 5–10 concurrent sessions – the resource requirements table scales up from there (4 CPU / 8GB for 10–20 sessions, 8+ CPU / 16GB+ beyond that).
Your registry credentials and your license key do different jobs. The credentials let you pull the image, and the key activates the Enterprise features once the container is running.
Deploy your first Docker container
Start by authenticating with the private registry using the credentials our team provides:
docker login registry.browserless.io
Pull the image, pinned to a version:
docker pull registry.browserless.io/browserless/browserless/enterprise:2.3.0
Run the container with two separate values: KEY is your Enterprise licence, and TOKEN is the API token clients authenticate with. Generate the token yourself – make it long and random (openssl rand -hex 32):
docker run \
--rm \
-p 3000:3000 \
--shm-size=2g \
-e KEY=your-enterprise-license-key \
-e TOKEN=your-secure-api-token \
registry.browserless.io/browserless/browserless/enterprise:2.3.0
Chrome uses /dev/shm for shared memory, and Docker caps that at 64MB by default, which crashes browsers under load. Setting --shm-size=2g prevents the most common startup failure. --ipc=host also works, but it shares the host IPC namespace, so you lose some isolation.
Once the container is up, verify it at three endpoints:
- API documentation at
http://localhost:3000/docs. - Health check at
http://localhost:3000/pressure?token=your-secure-api-token. - Metrics at
http://localhost:3000/metrics?token=your-secure-api-token.
Both health checks and metrics require the token you set – only /docs is open.
Connect your automation code to ws://localhost:3000/chromium?token=your-secure-api-token and you’re running browsers.
Configure for production
For anything beyond a quick test, use Docker Compose so your configuration lives in version control. The configuration reference covers every environment variable, but this setup handles most production needs:
services:
browserless:
image: registry.browserless.io/browserless/browserless/enterprise:2.3.0
container_name: browserless-enterprise
restart: unless-stopped
environment:
# Licence and authentication
- KEY=${BROWSERLESS_LICENSE_KEY}
- TOKEN=${BROWSERLESS_API_TOKEN}
# Capacity
- CONCURRENT=20
- QUEUED=30
- TIMEOUT=300000
# Public address used in reconnect and LiveURL links
- EXTERNAL=https://browsers.yourcompany.com
# Health-aware routing
- HEALTH=true
- MAX_MEMORY_PERCENT=90
- MAX_CPU_PERCENT=90
# Security: turn off what you don't use
- ENABLE_CORS=false
- ALLOW_GET=false
- ALLOW_FILE_PROTOCOL=false
# Persistence
- DATA_DIR=/user_data
- DOWNLOAD_DIR=/downloads
- METRICS_JSON_PATH=/metrics/metrics.json
ports:
- "3000:3000"
volumes:
- ./user_data:/user_data
- ./downloads:/downloads
- ./metrics:/metrics
# Chrome needs more than Docker's 64MB default
shm_size: "2g"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
healthcheck:
test:
[
"CMD-SHELL",
'curl -f "http://localhost:3000/pressure?token=$${TOKEN}" || exit 1',
]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: "4"
memory: 8G
reservations:
cpus: "2"
memory: 4G
Here's what each of those variables controls:
| Variable | What it controls |
|---|---|
KEY | Validates your Enterprise licence and unlocks the Enterprise features. Setting TOKEN instead of KEY is the classic mistake – the container runs, but as the base image. |
TOKEN | Authenticates every request and WebSocket connection. |
CONCURRENT | Maximum browser sessions running at once. |
QUEUED | Requests allowed to wait when concurrency is full. CONCURRENT=20 plus QUEUED=30 means 50 connections in flight; anything past that gets a 429. |
TIMEOUT | How long a session can run, in milliseconds, before it's killed. |
EXTERNAL | Your public-facing URL, used in reconnect and LiveURL links. |
The EXTERNAL variable is the one teams miss most often. Without it, reconnect and LiveURL links point at localhost:3000, which your clients likely cannot reach.
Set it to whatever your clients actually reach, usually your load balancer or ingress, including the protocol and the port if it's non-standard. Browserless then builds every reconnect and LiveURL link from that base. Our cloud-to-self-hosted migration guide walks through the rest of the switch.
Scale with high availability
To handle higher volume, run several containers behind a load balancer. Our NGINX guide uses least_conn so new sessions land on the least busy worker, and WebSocket connections stay pinned to that worker until they close.
Tune capacity per container with CONCURRENT and QUEUED, then add containers as your traffic grows. Each worker reports load through /pressure, so your balancer can route around a busy instance instead of queueing behind it. Set your proxy's read timeout above TIMEOUT – if the proxy gives up first, sessions drop silently with no error code. See timeouts.
Run browsers on a GPU
GPU acceleration helps with WebGL rendering, video, and canvas-heavy pages. GPU support currently covers NVIDIA hardware, and the licensed Enterprise image behaves the same as the open-source image here.
The host needs the NVIDIA driver and the NVIDIA Container Toolkit installed before the container can see the GPU. Once it's running, open chrome://gpu/ in a session to confirm hardware acceleration is actually on rather than falling back to SwiftShader.
Attach the GPU through environment variables rather than a runtime flag:
docker run -d --ipc=host -p 3000:3000 \
-e NVIDIA_VISIBLE_DEVICES=all \
-e NVIDIA_DRIVER_CAPABILITIES=all \
-e KEY=your-enterprise-license-key \
-e TOKEN=your-secure-api-token \
registry.browserless.io/browserless/browserless/enterprise:2.3.0
NVIDIA is the default container runtime on a properly configured host, so the GPU attaches automatically and you don’t need --gpus all. Add that flag explicitly only if NVIDIA isn’t set as your default runtime. The --ipc=host setting gives Chrome the shared memory it needs for stable GPU rendering.
Keep it secure and compliant
Self-hosting puts your security posture in your hands, and the image is built to support that. Run it behind your reverse proxy, turn off what you don't use – ALLOW_GET, ALLOW_FILE_PROTOCOL, and ENABLE_CORS all default to off and should stay that way – and mount KEY and TOKEN as Docker secrets rather than plain env vars. The best practices guide has the full checklist.
Never omit TOKEN. Without it, every route runs unauthenticated, including /function, which executes Puppeteer code supplied in the request body.
Licensing runs on time-limited software keys that work offline, so once you’ve pulled the image, the Enterprise build needs no external connections or callbacks to keep running – which is what makes air-gapped deployment work.
Two features reach the internet by design: residential proxies and CAPTCHA solving. Self-hosted doesn't ship proxy credentials – you bring your own proxy server, or buy access from us – so plan egress for both if you use them.
On certifications, Browserless holds a SOC 2 Type II report covering our own infrastructure and controls, from the shared fleet through private-cloud. When you self-host, the container is the same one we run, but the infrastructure it runs on is inside your audit boundary, not ours.
Although we aren’t HIPAA-certified on paper, if you need HIPAA, the self-hosted and private-cloud deployments are built for it.
Our formal position, as it appears in the Trust Center:
Browserless supports HIPAA-compatible deployments through our enterprise solutions, including private-cloud or self-hosted environments. A Business Associate Agreement (BAA) can be executed with customers using these deployments to process sensitive data, including PHI.
You can request our SOC 2 Type II report, a Data Processing Addendum (DPA), or a signed BAA from the Trust Center.
For multi-team access, issue separate tokens with admin, developer, viewer, or public roles rather than sharing one. Tokens persist to disk, so they survive container restarts. The token roles guide covers how to set that up.
Kubernetes and orchestration
Kubernetes is a supported platform, and plenty of teams run Browserless on it. There’s not an official Helm chart today, so you bring your own manifests and treat each Browserless container as a standard stateless workload behind a service.
Browserless has no database or side-car dependencies, so there's nothing to orchestrate beyond the pod itself. Run one pod per node where you can: browser sessions are CPU- and memory-hungry, and co-tenanting them is how you get shared-memory crashes.
The Docker Compose examples above translate directly to a Deployment and Service. Set your resource requests and limits to match the 2 CPU and 4GB minimum, mount the shared-memory sizing you'd get from --shm-size=2g, and scale replicas with a Horizontal Pod Autoscaler keyed off CPU – 70% utilisation is a reasonable starting target.
One catch to acknowledge: /pressure requires a token, which probes can't easily carry, so add an unauthenticated /healthz with a custom route and point your liveness and readiness probes at that.
If you need a reference manifest for your setup, our team can help you put one together – ask when you get your licence.
Updates and versioning
The Enterprise image is updated regularly with dependency and security patches. Pin to a specific version tag in production so upgrades happen when you choose, rather than on every pull, and coordinate larger version jumps with our team so you can test against your own integration suite first.
You can extend the base image with your own fonts, packages, or npm modules when you need them. Our guide to extending the image explains how.
Open source or Enterprise
Both images are self-hosted and share the same core engine, Puppeteer and Playwright connectivity, and REST APIs. The Enterprise image adds BrowserQL, stealth, and recording – which is why only its endpoints are a drop-in match for the cloud.
| Open source | Enterprise | |
|---|---|---|
| Cost | Free under SSPL-1.0 | Licensed, flat pricing |
| Browsers | Chromium, Chrome, Firefox, WebKit, Edge (one image per browser) | All five in a single container |
| Session management | Core | Advanced queueing and persistence |
| Live debugging and recording | No | Yes |
| Support | Community | Direct engineering support |
| Best for | Testing and small projects | Production and regulated workloads |
| BrowserQL | No | Yes |
| Stealth and CAPTCHA solving | No | Yes |
| OpenTelemetry | No | Yes |
| Token roles | No | Yes |
If a managed option would suit you better than running the containers yourself, private deployment puts Browserless on dedicated infrastructure that we operate for you.
Get started
Whether or not you self-host Browserless comes down to three decisions: which image you're licensed for, how you front it, and where the compliance boundary sits. Get those right and the automation code you already run in the cloud moves across unchanged.
Self-hosting starts with an Enterprise licence and registry access – tell us about your environment and we'll get you deploying.
Talk to our team about Enterprise
For a higher-level look at editions, licensing, and how self-hosted compares to the cloud and Private Deployment, see the self-hosted platform page.
Prefer to try the core features first? The open-source image is free, or you can sign up for a free cloud account to evaluate the APIs before you self-host.
Frequently asked questions
Can I run it fully air-gapped?
Yes. After you pull the image, the Enterprise build has no outbound dependency for licensing or usage tracking. Residential proxies and CAPTCHA solving are the exception, since both reach external services by design.
Is there a Helm chart?
Not an official one yet. Kubernetes is supported, and you deploy with your own manifests. Our team can help you build a reference for your environment.
Which GPUs are supported?
NVIDIA hardware. The licensed image and the open-source image handle GPUs the same way. Check that your host has the appropriate packages and software to support Docker and GPUs.
Can you support HIPAA workloads?
Yes. There's no formal HIPAA certification to hold, but self-hosted and private-cloud Enterprise deployments are HIPAA-ready: a BAA can be executed for these deployments where you're processing PHI. Our SOC 2 Type II report covers Browserless-operated infrastructure; self-hosted containers run inside your own boundary.
Will my cloud automation code work?
Yes. The API paths, BrowserQL queries, and CDP commands are identical. You change the connection host, set EXTERNAL, and bring your own proxy if you need one.