This article explains how to make the remote debugging port (9222) of a Chromium browser running inside a Docker container accessible from the host machine or other containers.
In E2E testing environments using Selenium Grid or Playwright, it’s common to run browsers inside Docker containers. If you want to use the Chrome DevTools Protocol (CDP) for debugging or automation, you need external access to the remote debugging port.
Normally, passing --remote-debugging-port=9222 and --remote-debugging-address=0.0.0.0 to Chromium should allow access from outside, but in recent versions this no longer works.
Even if you start Chromium with the following options, you cannot connect from outside:
chromium \
--remote-debugging-port=9222 \
--remote-debugging-address=0.0.0.0 \
--no-sandbox \
about:blank
When you check inside the container, it’s listening on 127.0.0.1:9222 instead of 0.0.0.0:9222:
$ netstat -tlnp | grep 9222
tcp 0 0 127.0.0.1:9222 0.0.0.0:* LISTEN 11/chromium
From Chromium M113/M114 onward, for security reasons, --remote-debugging-address=0.0.0.0 is internally forced to 127.0.0.1.
Chromium’s source code includes logic like the following:
// headless/lib/headless_browser_main_parts.cc
if (remote_debugging_address.IsIPv4AllZeros()) {
remote_debugging_address = net::IPAddress::IPv4Localhost();
} else if (remote_debugging_address.IsIPv6AllZeros()) {
remote_debugging_address = net::IPAddress::IPv6Localhost();
}
The reasoning is that the remote debugging port is a powerful feature that allows full control of the browser, and exposing it to a network is a serious security risk.
This is also reported in Chromium’s Bug Tracker (Issue 1425667), but the status is “WontFix,” meaning it’s treated as an intentional behavior change.
The official workarounds recommended by Chromium are:
In Docker environments, SSH tunneling is not very practical, so the simplest solution is port forwarding using socat.
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
chromium \
socat \
# ... other required packages
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
Since Chromium will only listen on 127.0.0.1, use an internal port (e.g., 9223):
# start-chrome.sh
exec /usr/lib/chromium/chromium \
--remote-debugging-port=9223 \
--remote-allow-origins=* \
--no-sandbox \
--disable-gpu \
about:blank
Use socat to forward from 0.0.0.0:9222 to 127.0.0.1:9223.
Example configuration when using supervisord:
[program:chromium]
command=/usr/local/bin/start-chrome.sh
autostart=true
autorestart=true
[program:socat-debug]
command=/usr/bin/socat TCP-LISTEN:9222,fork,reuseaddr TCP:127.0.0.1:9223
autostart=true
autorestart=true
docker run -p 9222:9222 your-image
After starting the container, you can verify from the host machine with:
$ curl -s http://127.0.0.1:9222/json
[ {
"description": "",
"devtoolsFrontendUrl": "https://chrome-devtools-frontend.appspot.com/...",
"id": "...",
"title": "about:blank",
"type": "page",
"url": "about:blank",
"webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/..."
} ]
If JSON is returned, it’s working.
When using Docker on Apple Silicon (M1/M2/M3) Macs, extra care is required.
If you build an x86_64 image by specifying --platform linux/amd64, emulation will be done via Rosetta 2. However, Chromium requires the SSE3 instruction set and will not run properly under Rosetta:
The hardware on this system lacks support for the sse3 instruction set.
For local development, you need to build natively for ARM64, and only build for x86_64 for production deployment:
# For local development (ARM64)
docker build -t my-image .
# For production deployment (x86_64)
docker build --platform linux/amd64 -t my-image .
--remote-debugging-address=0.0.0.0 is disabled for security reasonsThis change is an intentional security hardening in Chromium, and it’s unlikely to be reverted in the future.