You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Origin & Host Validation Block Usage in Docker Compose #3975
When running Lightpanda in Docker Compose, no containers can access the CDP endpoint. I believe this is due to Host validation, since no other container will ever be "localhost" and you cannot disable this check.
This is fixed by lightpanda-io/browser#3803, which shipped after 1.0.0: the --advertise-host name is now also accepted as a valid Host. With your compose file unchanged, ws://browser:9222 will work.
It will be in the next release. Until then you can use the lightpanda/browser:nightly image.
The 503 seems like a different issue than the host validation. The only time we return 503 is when --cdp-max-connections is reached.
with 1.0.1 are you still seeing rejected websocket host. Are you able to share the log output, maybe with --log-level info.
Also, how are you connecting (Playwright, Puppeteer, chromedp, ...). Is there reconnect logic? Are you able to share a reproducible playwright/puppeteer/chromedp/... script?
If you do a fresh docker compose up, what happens when you try to connect via a simple script? or maybe curl to /json/version?
There is retry logic, it retries every second. I'm using the Java Playwright library and trying to use the remote CDP. I fiddled with it more and I can curl it from the host OS and fetch that /json/version you mentioned. This is what the connection code looks like (Scala):
Thanks, this helps. The rejected websocket host warnings are gone, so the Host part does appear fixed by #3803. The 503 means all 16 (default) CDP slots are held by open WebSocket connections. There are only 4 disconnects in the log.
Is there a crawler log for the first failed attempt, before the 503 start?
On a failed attempt, does your retry call playwright.close() (or browser.close()) before creating a new Playwright? If not, every abandoned attempt keeps its Node driver and its WebSocket alive, which quickly fills up the slots.
Thanks, this helps. The rejected websocket host warnings are gone, so the Host part does appear fixed by #3803. The 503 means all 16 (default) CDP slots are held by open WebSocket connections. There are only 4 disconnects in the log.
1. Is there a crawler log for the first failed attempt, before the 503 start?
2. On a failed attempt, does your retry call playwright.close() (or browser.close()) before creating a new Playwright? If not, every abandoned attempt keeps its Node driver and its WebSocket alive, which quickly fills up the slots.
I cannot close the browser because it throws an exception during instantiation, so the object never exists for a second line of code to close with.
Here is a log of both the crawler and browser, where the crawler is limited to only one retry:
~/clones/crawler/infra main*
❯ docker compose logs crawler
crawler | com.microsoft.playwright.PlaywrightException: Error {
crawler | message='WebSocket error: ws://browser:9222/ 503 crawler | Too many connectionscrawler | name='Error
crawler | stack='Error: WebSocket error: ws://browser:9222/ 503 crawler | Too many connectionscrawler | at IncomingMessage.<anonymous> (/tmp/playwright-java-17035539771850931840/package/lib/coreBundle.js:39581:22)crawler | at IncomingMessage.emit (node:events:514:28)crawler | at emitCloseNT (node:internal/streams/destroy:148:10)crawler | at process.processTicksAndRejections (node:internal/process/task_queues:89:21)crawler | }crawler | Call log:crawler | - - <ws connecting> ws://browser:9222/crawler | - - <ws unexpected response> ws://browser:9222/ 503 crawler | Too many connectionscrawler | - - <ws error> ws://browser:9222/ error WebSocket was closed before the connection was establishedcrawler | - - <ws connect error> ws://browser:9222/ WebSocket was closed before the connection was establishedcrawler | - - <ws disconnected> ws://browser:9222/ code=1006 reason=crawler | crawler | failed to connect to playwright browser ws://browser:9222, retryingcrawler | com.microsoft.playwright.PlaywrightException: Error {crawler | message='WebSocket error: ws://browser:9222/ 503
crawler | Too many connections
crawler | name='Errorcrawler | stack='Error: WebSocket error: ws://browser:9222/ 503
crawler | Too many connections
crawler | at IncomingMessage.<anonymous> (/tmp/playwright-java-17035539771850931840/package/lib/coreBundle.js:39581:22)
crawler | at IncomingMessage.emit (node:events:514:28)
crawler | at emitCloseNT (node:internal/streams/destroy:148:10)
crawler | at process.processTicksAndRejections (node:internal/process/task_queues:89:21)
crawler | }
crawler | Call log:
crawler | - - <ws connecting> ws://browser:9222/
crawler | - - <ws unexpected response> ws://browser:9222/ 503
crawler | Too many connections
crawler | - - <ws error> ws://browser:9222/ error WebSocket was closed before the connection was established
crawler | - - <ws connect error> ws://browser:9222/ WebSocket was closed before the connection was established
crawler | - - <ws disconnected> ws://browser:9222/ code=1006 reason=
crawler |
crawler | failed to connect to playwright browser ws://browser:9222, retrying
crawler | Exception in thread "main" java.util.NoSuchElementException: None.get
crawler | at scala.None$.get(Option.scala:628)
crawler | at scala.None$.get(Option.scala:628)
crawler | at Main$package$.Main(Main.scala:51)
crawler | at Main.main(Main.scala:37)
~/clones/crawler/infra main*
❯ docker compose logs browser
browser |$time=1792883499912 $scope=telemetry $level=info $msg="telemetry status" disabled=true
browser |$time=1792883499912 $scope=app $level=debug $msg="startup" mode=serve snapshot=true
browser |$time=1792883499912 $scope=note $level=note $msg="server running" address=0.0.0.0:9222 max_connections=1872 max_browser_connections=16
Thanks. I'm not able to reproduce the issue, and I'm not sure what's going on. I used your exact serve command and Playwright's connectOverCDP("ws://browser:9222") from another container on the same network connects on the first try.
A 503 "Too many connections" only happens when all 16 CDP slots hold open WebSocket connections. Opening a connection doesn't log anything at info level (only disconnects do), so this wouldn't show up in the browser log. Some client seems to be holding 16 connections before the crawler's attempt arrives. Could you:
Share the full compose file, including any healthchecks and other services that can reach browser:9222.
When the 503s start, run curl http://:9222/metrics and paste the output. serve_active_connections shows how many CDP sessions are open.
Does the crawler connects once or once per thread/task (e.g., several connectOverCDP calls running at the same time).
Activity
cdebled commentedon Oct 15, 2026
This is fixed by lightpanda-io/browser#3803, which shipped after 1.0.0: the
--advertise-hostname is now also accepted as a valid Host. With your compose file unchanged,ws://browser:9222will work.It will be in the next release. Until then you can use the
lightpanda/browser:nightlyimage.karlseguin commentedon Oct 15, 2026
1.0.1 was released
larkwiot commentedon Oct 17, 2026
I tried the
1.0.1andnightlytags and still get the same behavior. Sharing the port to the host and curling on localhost gets 503.karlseguin commentedon Oct 19, 2026
The 503 seems like a different issue than the host validation. The only time we return 503 is when
--cdp-max-connectionsis reached.with 1.0.1 are you still seeing
rejected websocket host. Are you able to share the log output, maybe with--log-level info.Also, how are you connecting (Playwright, Puppeteer, chromedp, ...). Is there reconnect logic? Are you able to share a reproducible playwright/puppeteer/chromedp/... script?
If you do a fresh
docker compose up, what happens when you try to connect via a simple script? or maybe curl to /json/version?larkwiot commentedon Oct 19, 2026
There is retry logic, it retries every second. I'm using the Java Playwright library and trying to use the remote CDP. I fiddled with it more and I can curl it from the host OS and fetch that
/json/versionyou mentioned. This is what the connection code looks like (Scala):And here are the logs:
karlseguin commentedon Oct 20, 2026
Thanks, this helps. The rejected websocket host warnings are gone, so the Host part does appear fixed by #3803. The 503 means all 16 (default) CDP slots are held by open WebSocket connections. There are only 4 disconnects in the log.
larkwiot commentedon Oct 24, 2026
I cannot close the browser because it throws an exception during instantiation, so the object never exists for a second line of code to close with.
Here is a log of both the crawler and browser, where the crawler is limited to only one retry:
karlseguin commentedon Oct 26, 2026
Thanks. I'm not able to reproduce the issue, and I'm not sure what's going on. I used your exact
servecommand and Playwright'sconnectOverCDP("ws://browser:9222")from another container on the same network connects on the first try.A 503 "Too many connections" only happens when all 16 CDP slots hold open WebSocket connections. Opening a connection doesn't log anything at info level (only disconnects do), so this wouldn't show up in the browser log. Some client seems to be holding 16 connections before the crawler's attempt arrives. Could you:
serve_active_connectionsshows how many CDP sessions are open.Does the crawler connects once or once per thread/task (e.g., several connectOverCDP calls running at the same time).