A minimal headless Chromium image built specifically as a Chrome CDP sidecar for Karakeep.
Karakeep requires a headless Chrome instance reachable via the Chrome DevTools Protocol (CDP). Chromium v112+ removed --remote-debugging-address, so Chrome always binds DevTools to 127.0.0.1 — unreachable from other Docker containers. This image solves that with a built-in CDP proxy that rewrites WebSocket URLs and exposes port 9222 on all interfaces.
- GitHub Container Registry:
ghcr.io/todd2982/alpine-chrome
Add this as a sidecar in your docker-compose.yml:
services:
chrome:
image: ghcr.io/todd2982/alpine-chrome:latest
restart: unless-stopped
cap_add:
- SYS_ADMIN
karakeep:
image: ghcr.io/karakeep-app/karakeep:release
environment:
BROWSER_WEB_URL: http://chrome:9222
depends_on:
- chromeDefault behavior: The image ships with
--no-sandboxenabled by default (viaCHROMIUM_FLAGS) because most Docker environments do not grantSYS_ADMINor a permissive seccomp profile. If you can provide either, overrideCHROMIUM_FLAGSto remove it — see Security below.
The entrypoint (docker-entrypoint.sh) does three things:
- Starts Chromium bound to
127.0.0.1:9223(internal only) - Runs a Python CDP proxy (
cdp-proxy.py) on0.0.0.0:9222that:- Rewrites WebSocket URLs in
/jsonand/json/versionresponses so CDP clients receive usable addresses - Transparently proxies WebSocket CDP connections
- Rewrites WebSocket URLs in
- Monitors both processes and exits if either crashes
This is necessary because Chromium v112+ ignores --remote-debugging-address and always binds DevTools to localhost.
Chrome sandboxing requires special Docker configuration. Three options (best to worst):
The image defaults to --no-sandbox so it works out of the box in standard Docker environments. To use a hardened configuration, override CHROMIUM_FLAGS to remove --no-sandbox and grant Chrome the sandbox it needs:
docker run -d -p 9222:9222 \
--security-opt seccomp=$(pwd)/chrome.json \
-e CHROMIUM_FLAGS="--disable-software-rasterizer --disable-dev-shm-usage" \
ghcr.io/todd2982/alpine-chromeThe chrome.json seccomp profile (from Jessie Frazelle) lets Chrome sandbox properly without requiring SYS_ADMIN.
docker run -d -p 9222:9222 \
--cap-add=SYS_ADMIN \
-e CHROMIUM_FLAGS="--disable-software-rasterizer --disable-dev-shm-usage" \
ghcr.io/todd2982/alpine-chromedocker run -d -p 9222:9222 ghcr.io/todd2982/alpine-chromeWorks in standard Docker environments without any extra capabilities. Only use in trusted/isolated environments where the container is the trust boundary.
| Variable | Default | Description |
|---|---|---|
CHROMIUM_FLAGS |
--no-sandbox --disable-software-rasterizer --disable-dev-shm-usage |
Extra flags appended to every Chrome invocation. Override to remove --no-sandbox when using SYS_ADMIN or a seccomp profile. |
HOME |
/tmp |
Required for Chromium to function |
CHROME_BIN |
/usr/bin/chromium-browser |
Path to Chrome binary |
CHROME_PATH |
/usr/lib/chromium/ |
Chrome library path |
Override CHROMIUM_FLAGS to customize Chrome startup flags. For example, to enable sandbox with SYS_ADMIN:
docker run -d -p 9222:9222 \
--cap-add=SYS_ADMIN \
-e CHROMIUM_FLAGS="--disable-software-rasterizer --disable-dev-shm-usage" \
ghcr.io/todd2982/alpine-chromeIf you need raw Chrome access without the CDP proxy:
docker run --rm -it --entrypoint "" ghcr.io/todd2982/alpine-chrome \
chromium-browser --headless --no-sandbox ...docker build -t ghcr.io/todd2982/alpine-chrome:latest ../test.shThe build workflow (.github/workflows/build.yml) runs on:
- Pull requests
- Weekly schedule (Thursday 04:25 UTC)
- Manual dispatch
It builds the image, runs tests, extracts the Chromium version for tagging, and pushes to GHCR on master.
- Karakeep — the bookmark manager this image is built for
- Chrome DevTools Protocol
- Chromium command-line switches
- Issues