From d1a5ea2024a3b8e86d6595ae9f018545b3a6068c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 26 Apr 2026 05:50:52 +0100 Subject: [PATCH] fix(docker): disable bonjour by default for compose --- CHANGELOG.md | 3 +++ docker-compose.yml | 3 +++ docs/gateway/bonjour.md | 4 ++++ docs/gateway/discovery.md | 3 +++ docs/install/docker.md | 12 ++++++++++++ scripts/docker/setup.sh | 7 +++++++ src/docker-setup.e2e.test.ts | 20 ++++++++++++++++++++ 7 files changed, 52 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ef13eb7687..8de99f79876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -145,6 +145,9 @@ Docs: https://docs.openclaw.ai and `media://inbound/...` markers from pruned model replay context so stale media refs are not rehydrated as fresh prompt images. Fixes #71868. Thanks @jmeadlock. +- Docker/Bonjour: disable Bonjour/mDNS advertising by default for bundled + Compose gateways on bridge networking, while keeping host/macvlan opt-in with + `OPENCLAW_DISABLE_BONJOUR=0`. Fixes #71879. Thanks @gbballpack. - CLI/status: label the OpenClaw Serve/Funnel setting as `Tailscale exposure` and show daemon state separately when available, so `gateway.tailscale.mode: "off"` no longer reads like the Tailscale daemon is stopped. Fixes #71790. Thanks @pesvobodak. - Plugins/Bonjour: stop ciao mDNS watchdog failures from looping forever when the advertiser stays stuck in `probing` or `announcing`; Bonjour now disables itself for the current Gateway process after repeated failed restarts while the Gateway keeps running. Fixes #69011. Thanks @siddharthaagarwalofficial-ux, @FiredMosquito831, and @spikefcz. - Gateway/Fly.io: seed Control UI allowed origins from the actual runtime bind and port so CLI-driven non-loopback starts do not crash before config exists. Fixes #71823. diff --git a/docker-compose.yml b/docker-compose.yml index 0a55b342e92..dee895d469a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,9 @@ services: TERM: xterm-256color OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN:-} OPENCLAW_ALLOW_INSECURE_PRIVATE_WS: ${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-} + # Docker bridge networks usually do not carry mDNS multicast reliably. + # Set OPENCLAW_DISABLE_BONJOUR=0 only on host/macvlan/mDNS-capable networks. + OPENCLAW_DISABLE_BONJOUR: ${OPENCLAW_DISABLE_BONJOUR:-1} CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY:-} CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY:-} CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE:-} diff --git a/docs/gateway/bonjour.md b/docs/gateway/bonjour.md index 0f0e8e5cd06..0a3582f27be 100644 --- a/docs/gateway/bonjour.md +++ b/docs/gateway/bonjour.md @@ -160,6 +160,9 @@ The log includes browser state transitions and result‑set changes. container bridges, WSL, or interface churn can leave the ciao advertiser in a non-announced state. OpenClaw retries a few times and then disables Bonjour for the current Gateway process instead of restarting the advertiser forever. +- **Docker bridge networking**: bundled Docker Compose disables Bonjour by + default with `OPENCLAW_DISABLE_BONJOUR=1`. Set it to `0` only for host, + macvlan, or another mDNS-capable network. - **Sleep / interface churn**: macOS may temporarily drop mDNS results; retry. - **Browse works but resolve fails**: keep machine names simple (avoid emojis or punctuation), then restart the Gateway. The service instance name derives from @@ -178,6 +181,7 @@ sequences (e.g. spaces become `\032`). - `openclaw plugins disable bonjour` disables LAN multicast advertising by disabling the bundled plugin. - `openclaw plugins enable bonjour` restores the default LAN discovery plugin. - `OPENCLAW_DISABLE_BONJOUR=1` disables LAN multicast advertising without changing plugin config; accepted truthy values are `1`, `true`, `yes`, and `on` (legacy: `OPENCLAW_DISABLE_BONJOUR`). +- Docker Compose sets `OPENCLAW_DISABLE_BONJOUR=1` by default for bridge networking; override with `OPENCLAW_DISABLE_BONJOUR=0` only when mDNS multicast is available. - `gateway.bind` in `~/.openclaw/openclaw.json` controls the Gateway bind mode. - `OPENCLAW_SSH_PORT` overrides the SSH port when `sshPort` is advertised (legacy: `OPENCLAW_SSH_PORT`). - `OPENCLAW_TAILNET_DNS` publishes a MagicDNS hint in TXT when mDNS full mode is enabled (legacy: `OPENCLAW_TAILNET_DNS`). diff --git a/docs/gateway/discovery.md b/docs/gateway/discovery.md index 100a60b3b3c..a8033af764f 100644 --- a/docs/gateway/discovery.md +++ b/docs/gateway/discovery.md @@ -86,6 +86,9 @@ Security notes: Disable/override: - `OPENCLAW_DISABLE_BONJOUR=1` disables advertising. +- Docker Compose defaults `OPENCLAW_DISABLE_BONJOUR=1` because bridge networks + usually do not carry mDNS multicast reliably; use `0` only on host, macvlan, + or another mDNS-capable network. - `gateway.bind` in `~/.openclaw/openclaw.json` controls the Gateway bind mode. - `OPENCLAW_SSH_PORT` overrides the SSH port advertised when `sshPort` is emitted. - `OPENCLAW_TAILNET_DNS` publishes a `tailnetDns` hint (MagicDNS). diff --git a/docs/install/docker.md b/docs/install/docker.md index acbf91a9740..741d8a05bcd 100644 --- a/docs/install/docker.md +++ b/docs/install/docker.md @@ -131,6 +131,7 @@ The setup script accepts these optional environment variables: | `OPENCLAW_HOME_VOLUME` | Persist `/home/node` in a named Docker volume | | `OPENCLAW_SANDBOX` | Opt in to sandbox bootstrap (`1`, `true`, `yes`, `on`) | | `OPENCLAW_DOCKER_SOCKET` | Override Docker socket path | +| `OPENCLAW_DISABLE_BONJOUR` | Disable Bonjour/mDNS advertising (defaults to `1` for Docker) | ### Health checks @@ -165,6 +166,17 @@ Use bind mode values in `gateway.bind` (`lan` / `loopback` / `custom` / `tailnet` / `auto`), not host aliases like `0.0.0.0` or `127.0.0.1`. +### Bonjour / mDNS + +Docker bridge networking usually does not forward Bonjour/mDNS multicast +(`224.0.0.251:5353`) reliably. The bundled Compose setup therefore defaults +`OPENCLAW_DISABLE_BONJOUR=1` so the Gateway does not crash-loop or repeatedly +restart advertising when the bridge drops multicast traffic. + +Use the published Gateway URL, Tailscale, or wide-area DNS-SD for Docker hosts. +Set `OPENCLAW_DISABLE_BONJOUR=0` only when running with host networking, macvlan, +or another network where mDNS multicast is known to work. + ### Storage and persistence Docker Compose bind-mounts `OPENCLAW_CONFIG_DIR` to `/home/node/.openclaw` and diff --git a/scripts/docker/setup.sh b/scripts/docker/setup.sh index fe630101588..be076899251 100755 --- a/scripts/docker/setup.sh +++ b/scripts/docker/setup.sh @@ -275,6 +275,7 @@ export OPENCLAW_WORKSPACE_DIR export OPENCLAW_GATEWAY_PORT="${OPENCLAW_GATEWAY_PORT:-18789}" export OPENCLAW_BRIDGE_PORT="${OPENCLAW_BRIDGE_PORT:-18790}" export OPENCLAW_GATEWAY_BIND="${OPENCLAW_GATEWAY_BIND:-lan}" +export OPENCLAW_DISABLE_BONJOUR="${OPENCLAW_DISABLE_BONJOUR:-1}" export OPENCLAW_IMAGE="$IMAGE_NAME" export OPENCLAW_DOCKER_APT_PACKAGES="${OPENCLAW_DOCKER_APT_PACKAGES:-}" export OPENCLAW_EXTENSIONS="${OPENCLAW_EXTENSIONS:-}" @@ -458,6 +459,7 @@ upsert_env "$ENV_FILE" \ OPENCLAW_GATEWAY_PORT \ OPENCLAW_BRIDGE_PORT \ OPENCLAW_GATEWAY_BIND \ + OPENCLAW_DISABLE_BONJOUR \ OPENCLAW_GATEWAY_TOKEN \ OPENCLAW_IMAGE \ OPENCLAW_EXTRA_MOUNTS \ @@ -509,6 +511,11 @@ echo "==> Onboarding (interactive)" echo "Docker setup pins Gateway mode to local." echo "Gateway runtime bind comes from OPENCLAW_GATEWAY_BIND (default: lan)." echo "Current runtime bind: $OPENCLAW_GATEWAY_BIND" +if is_truthy_value "$OPENCLAW_DISABLE_BONJOUR"; then + echo "Bonjour/mDNS advertising: disabled for Docker bridge networking (OPENCLAW_DISABLE_BONJOUR=$OPENCLAW_DISABLE_BONJOUR)." +else + echo "Bonjour/mDNS advertising: enabled (OPENCLAW_DISABLE_BONJOUR=$OPENCLAW_DISABLE_BONJOUR)." +fi echo "Gateway token: $OPENCLAW_GATEWAY_TOKEN" echo "Tailscale exposure: Off (use host-level tailnet/Tailscale setup separately)." echo "Install Gateway daemon: No (managed by Docker Compose)" diff --git a/src/docker-setup.e2e.test.ts b/src/docker-setup.e2e.test.ts index 1dbfcb4e044..48c3d27d727 100644 --- a/src/docker-setup.e2e.test.ts +++ b/src/docker-setup.e2e.test.ts @@ -222,6 +222,7 @@ describe("scripts/docker/setup.sh", () => { expect(envFile).toContain("OPENCLAW_DOCKER_APT_PACKAGES=ffmpeg build-essential"); expect(envFile).toContain("OPENCLAW_EXTRA_MOUNTS="); expect(envFile).toContain("OPENCLAW_HOME_VOLUME=openclaw-home"); // pragma: allowlist secret + expect(envFile).toContain("OPENCLAW_DISABLE_BONJOUR=1"); const extraCompose = await readFile( join(activeSandbox.rootDir, "docker-compose.extra.yml"), "utf8", @@ -240,6 +241,18 @@ describe("scripts/docker/setup.sh", () => { expect(log).not.toContain("run --rm openclaw-cli onboard --mode local --no-install-daemon"); }); + it("persists explicit Docker Bonjour opt-in overrides", async () => { + const activeSandbox = requireSandbox(sandbox); + + const result = runDockerSetup(activeSandbox, { + OPENCLAW_DISABLE_BONJOUR: "0", + }); + + expect(result.status).toBe(0); + const envFile = await readFile(join(activeSandbox.rootDir, ".env"), "utf8"); + expect(envFile).toContain("OPENCLAW_DISABLE_BONJOUR=0"); + }); + it("avoids shared-network openclaw-cli before the gateway is started", async () => { const activeSandbox = requireSandbox(sandbox); @@ -534,6 +547,13 @@ describe("scripts/docker/setup.sh", () => { expect(compose).toContain('"gateway"'); }); + it("keeps docker-compose gateway Bonjour advertising disabled by default", async () => { + const compose = await readFile(join(repoRoot, "docker-compose.yml"), "utf8"); + expect( + compose.match(/OPENCLAW_DISABLE_BONJOUR: \$\{OPENCLAW_DISABLE_BONJOUR:-1\}/g), + ).toHaveLength(1); + }); + it("keeps docker-compose CLI network namespace settings in sync", async () => { const compose = await readFile(join(repoRoot, "docker-compose.yml"), "utf8"); expect(compose).toContain('network_mode: "service:openclaw-gateway"');