Self-Hosted Browser Automation APIs on Your Infrastructure
Learn how to run a browser automation API inside your own network, secure it, scale it, and decide when a hosted screenshot API is simpler.
Short answer: a self-hosted browser automation API is an HTTP or WebSocket service that your application calls while browser processes run on infrastructure you control. You deploy the browser server in a VM, container platform, Kubernetes cluster, VPC or on-premises network; your code sends URLs or browser commands to it; the service returns screenshots, PDFs, rendered HTML or extracted data.
This model is useful when page traffic, credentials, screenshots or results must stay inside a specific network boundary. It also means your team owns browser updates, authentication, concurrency, queues, capacity planning, monitoring and incident response.
How the deployment works
The common request path is:
- Your application authenticates to an internal browser endpoint.
- The endpoint allocates a browser process or session.
- Chromium, Chrome, Firefox, WebKit or Edge loads the target page.
- Your client drives the page through REST, CDP, Playwright or Puppeteer.
- The service returns binary output such as PNG or PDF, or JSON/HTML data.
Browserless documents this pattern with an open-source Docker image that supports Puppeteer and Playwright over WebSocket and includes REST APIs for screenshots, PDFs and scraping. Its API reference documents REST plus WebSocket connections for CDP, Playwright and Puppeteer. See the Browserless documentation and open-source repository for the current image and endpoint details.
When self-hosting is a good fit
| Requirement | Why self-hosting helps | Cost you take on |
|---|---|---|
| Private data location | Browser traffic and output stay in your VPC or data centre. | You must secure egress, DNS, logs and storage. |
| Network allowlists | Give browsers access to internal sites or fixed outbound addresses. | You operate proxies, firewall rules and certificates. |
| Predictable workload | Reserve capacity for known concurrency. | Idle capacity and scaling are your responsibility. |
| Custom browser control | Install approved browsers, fonts and extensions. | You patch images and validate browser changes. |
| Regulated environment | Keep credentials and page content under your controls. | You still need access controls, retention and audit processes. |
A hosted API is usually simpler when you need bursty capacity, managed proxies, or minimal operations. Self-hosting is an infrastructure decision, not a guarantee that every cloud feature or endpoint is available locally.
Run Browserless with Docker
Browserless is a documented example of this deployment pattern. The exact image tag and browser image should match the client and browser you intend to use. The open-source images support Linux amd64 and arm64; Chrome and Edge images are amd64-only, while the ARM multi-browser image includes Chromium, Firefox and WebKit.
1. Start a protected container
docker run --name browserless \
-p 3000:3000 \
-e TOKEN=replace-with-a-long-random-token \
-e CONCURRENT=5 \
ghcr.io/browserless/chromium:latest
Set a real secret in your secret manager rather than committing it to a compose file. Keep port 3000 on a private network or behind an authenticated reverse proxy. Browserless warns that the open-source image does not generate a token automatically: if TOKEN is missing, endpoints remain unauthenticated, including /function, which can execute supplied Puppeteer code.
2. Connect with Playwright over CDP
import { chromium } from 'playwright';
const browser = await chromium.connectOverCDP(
'ws://localhost:3000/chromium?token=replace-with-a-long-random-token'
);
const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'example.png', fullPage: true });
await browser.close();
Use the endpoint and browser type that match the image you deployed. For a remote host, use wss:// through a TLS-terminating proxy and keep the token out of browser-side code.
3. Capture through REST
REST paths and parameters vary by deployment and product edition. Check the current screenshot API reference before wiring a production client. A typical request pattern is:
curl -fL 'http://localhost:3000/screenshot?token=replace-with-a-long-random-token' \
-H 'Content-Type: application/json' \
--data '{"url":"https://example.com","options":{"fullPage":true}}' \
-o example.png
The API reference also covers rendered content, PDFs, scraping and direct WebSocket access. Treat the documentation for your installed version as authoritative.
Secure the endpoint before connecting applications
- Require a token. Set
TOKENand rotate it through a secret manager. - Restrict network exposure. Allow only application subnets, VPN clients or a private load balancer.
- Put TLS at the edge. Use a reverse proxy for HTTPS/WSS, certificate renewal and request limits.
- Disable unused features. Especially endpoints that execute arbitrary browser code.
- Control outbound access. Browsers can reach every host allowed by your network; apply egress policy and DNS controls.
- Protect logs. URLs, headers, cookies and extracted content may contain personal or secret data.
- Separate tenants. Use isolated tokens, queues or deployments when workloads have different trust levels.
REST, CDP, Playwright and Puppeteer: choose the interface
| Interface | Use it when | Trade-off |
|---|---|---|
| REST | You need simple screenshots, PDFs, HTML or extraction. | Less control over long browser workflows. |
| CDP WebSocket | Your client already speaks Chrome DevTools Protocol. | Chrome-focused and lower-level. |
| Playwright WebSocket/CDP | You need reliable waits, contexts and multi-browser APIs. | Client and server versions must be compatible. |
| Puppeteer WebSocket | Your existing automation is Puppeteer-based. | Mostly Chromium-oriented. |
Feature boundaries and licensing
Self-hosted does not mean that every capability offered by a vendor’s cloud is present in the Docker image. Browserless identifies six advanced REST endpoints as cloud-only: /unblock, /smart-scrape, /search, /map, /crawl and /agent/run. It lists /scrape for structured extraction and /content for rendered HTML as self-hosted alternatives. Self-hosted customers bring their own proxy rather than using a managed cloud proxy.
Browserless says its open-source image is licensed under SSPL-1.0 and is free for open-source projects, prototyping and evaluation. It says closed-source commercial products or closed-source CI require a commercial license. Commercial licensing and Enterprise offerings have different rights and features, so review the license and current product terms for your use case before deployment.
Capacity, queues and scaling
Browser sessions are resource-heavy and workload-dependent. A page with video, large images, many JavaScript bundles or PDF rendering consumes more CPU and memory than a static page. Start with a queue and explicit timeouts instead of allowing unbounded sessions.
Vendor sizing guidance
Browserless publishes these illustrative figures: 5–10 concurrent sessions on 2 CPU and 4 GB RAM; 10–20 sessions on 4 CPU and 8 GB RAM; 20–50 sessions on 8+ CPU and 16+ GB RAM. These are product guidance, not independent benchmarks. Measure your own pages, browser mix and timeout policy.
Operational checklist
- Set a maximum session concurrency and queue depth.
- Apply navigation, action and overall job timeouts.
- Recycle contexts and close pages in every success and error path.
- Monitor CPU, memory, file descriptors, crashes, queue age and timeout rate.
- Load-balance across containers only after session limits are enforced per container.
- Warm capacity for scheduled bursts and autoscale for variable traffic.
- Pin image versions, test browser upgrades, and keep a rollback image.
- Health-check both the HTTP endpoint and an actual browser launch.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| 401 or unauthenticated requests | Missing, incorrect or rotated token. | Pass the configured token and verify the proxy forwards it. |
| Connection refused on port 3000 | Container stopped, port unpublished or firewall blocked. | Inspect container logs, publish the port and check network rules. |
| WebSocket closes immediately | Wrong browser path, protocol, token or client/server version. | Match the image, browser endpoint and Playwright/Puppeteer client. |
| Navigation timeout | Slow page, blocked DNS, proxy failure or insufficient resources. | Test DNS and egress from the container, raise a bounded timeout and reduce concurrency. |
| Blank screenshot | Capture happened before rendering or the page requires a wait. | Wait for a selector or network idle and confirm the page is reachable inside the container. |
| Browser crashes under load | Memory pressure, too many contexts or heavy pages. | Lower concurrency, cap page size, add memory and inspect crash logs. |
| Missing fonts or different layout | Fonts are absent from the image or viewport differs. | Install approved fonts in a derived image and set a fixed viewport/device scale. |
| Cloud endpoint unavailable | The endpoint is not included in self-hosted deployment. | Use the documented self-hosted alternative or select a deployment that includes it. |
Performance, reliability and cost
Measure end-to-end latency, browser launch time, navigation time, render time and output transfer separately. Reuse a browser process where supported, but isolate user data in separate contexts. Cache deterministic pages when freshness allows it. For reliability, make jobs idempotent, retry only transient failures with backoff, and record the URL, browser version, deployment revision and failure stage.
Your cost includes compute, persistent storage for logs or artifacts, outbound bandwidth, proxy traffic, observability, patching and engineering time. A small server may be economical for steady internal workloads; bursty workloads can require enough idle capacity to absorb peaks. A generic mini PC for a Docker server is an infrastructure choice, not a guarantee of any session count: validate capacity with your pages.
Or skip the browser setup
If you only need clean website screenshots or PDFs, ScreenshotNeo provides a hosted GET endpoint and an MCP server for AI agents. It removes cookie and consent banners, newsletter popups and chat widgets before capture; bot checks, blank pages and failed loads are not billed; and each response reports the page verdict and billing status in headers. Its MCP tools are take_screenshot, get_page_info and capture_pdf.
See the ScreenshotNeo API documentation for the full option set. A one-call capture looks like this:
curl -G 'https://api.screenshotneo.com/v1/shot' \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
import requests
r = requests.get('https://api.screenshotneo.com/v1/shot', params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'}, timeout=90)
r.raise_for_status()
open('shot.webp', 'wb').write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
It supports full-page and element captures, device presets or custom viewports, retina scale, dark mode, PDF options, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed image links, asynchronous jobs, webhooks, bulk capture and a usage API. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Can I self-host Browserless?
Yes. Browserless documents an open-source Docker deployment. You operate the infrastructure, network, credentials and updates, and must follow the applicable SSPL or commercial licensing terms.
Does self-hosting keep browser traffic private?
It can keep traffic inside your controlled network boundary, provided your proxy, DNS, egress rules, logs and upstream destinations are configured accordingly. Deployment location alone is not a security control.
Do I need Kubernetes?
No. A single Docker host is enough for a small workload. Kubernetes or another scheduler becomes useful when you need rolling updates, multiple replicas, autoscaling and service discovery.
How do I choose between REST and Playwright?
Use REST for discrete operations such as screenshots and PDFs. Use Playwright or Puppeteer when you need multi-step interactions, authenticated contexts or detailed browser control.
What should I test before production?
Test representative pages, browser versions, concurrency, timeouts, proxy behavior, authentication, failure retries, artifact retention and upgrade rollback.


