April 19, 2026 • Version: v3.9

Dashboard von macOS Browser mit Docker Gateway nicht erreichbar

Wenn der OpenClaw Gateway an Loopback innerhalb von Docker auf macOS bindet, ist das Dashboard vom Host-Browser aus aufgrund der Container-Netzwerkisolierung nicht erreichbar.

🔍 Symptome

Das OpenClaw-Dashboard ist nach einem Upgrade auf Version 3.9 vom macOS-Host-Browser aus nicht mehr erreichbar. Die Telegram-Bot-Integration funktioniert weiterhin normal.

Netzwerk-Level-Fehler-Manifestation

$ curl -v http://127.0.0.1:18789/
* Connected to 127.0.0.1 port 18789
> GET / HTTP/1.1
> Host: 127.0.0.1:18789
>
* Empty reply from server
* Connection died, errnum=0, curlcode=22
curl: (52) Empty reply from server

Verhalten an allen Endpoints

# All routes return identical empty response
$ curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:18789/
000

$ curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:18789/healthz
000

$ curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:18789/ui/
000

Container-Health bestätigt laufenden Dienst

$ docker compose ps
NAME                IMAGE                  COMMAND                  SERVICE          CREATED          STATUS                    PORTS
openclaw-gateway    openclaw/gateway       "/entrypoint.sh gate…"   gateway          10 minutes ago  Up (healthy)             18789/tcp

Diagnose: Loopback-Binding-Verifizierung

# Exec into container to verify listening address
$ docker exec -it openclaw-gateway sh -c "netstat -tlnp | grep 18789"
tcp        0      0 127.0.0.1:18789          0.0.0.0:*               LISTEN      1/openclaw-gateway

# From inside container, the service responds
$ docker exec -it openclaw-gateway curl -s http://127.0.0.1:18789/healthz
{"status":"ok","version":"3.9.0"}

Geräte-Pairing wird nie registriert

$ docker compose run --rm openclaw-cli devices list
[]

Es erscheinen keine ausstehenden Geräteanfragen, da die Browserverbindung nie mit dem Gateway hergestellt wird.

🧠 Ursache

Docker Desktop für macOS Netzwerk-Architektur

Die Grundursache liegt in einem fundamentalen Unterschied, wie Docker Desktop das Container-Netzwerk auf macOS im Vergleich zu Linux-Hosts handhabt.

Auf Linux Docker-Hosts, wenn ein Container einen Dienst an 127.0.0.1:18789 bindet, ist der Port vom Host aus erreichbar, da das Docker-Bridge-Netzwerk die Loopback-Schnittstelle des Hosts teilt. Bei Docker Desktop für macOS werden Container jedoch in einer leichtgewichtigen Linux-VM ausgeführt, was eine Netzwerkisolierungsgrenze erzeugt:

┌─────────────────────────────────────────────────────────────────┐
│                    macOS Host                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │              Docker Desktop Linux VM                     │    │
│  │   ┌─────────────────────────────────────────────────┐   │    │
│  │   │           Container Network (bridge)             │   │    │
│  │   │                                                  │   │    │
│  │   │   openclaw-gateway:127.0.0.1:18789               │   │    │
│  │   │                                                  │   │    │
│  │   └─────────────────────────────────────────────────┘   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  Browser: http://127.0.0.1:18789 ──┐                            │
│                                    │ BLOCKED                    │
│                                    ↓                            │
└─────────────────────────────────────────────────────────────────┘

Binding-Konfigurationsanalyse

Die Umgebungsvariable OPENCLAW_GATEWAY_BIND steuert die Listen-Adresse:

Bind-WertInnerhalb des ContainersVom macOS-HostAnmerkungen
loopback oder 127.0.0.1✓ Erreichbar✗ BlockiertDienst bindet nur an Container-Loopback
0.0.0.0✓ Erreichbar✓ ErreichbarLauscht auf allen Schnittstellen inkl. Docker-Virtueller Schnittstelle
Unspecified (default)Variiert je nach VersionVariiertOft Standard auf Loopback aus Sicherheitsgründen

Versions-Regressionsanalyse

Die Regression zwischen v2.9 und v3.9 deutet auf eine Konfigurationsänderung hin:

  1. v2.9: Gateway war möglicherweise standardmäßig an 0.0.0.0 gebunden, oder die Port-Veröffentlichung war konfiguriert
  2. v3.9: Standard wurde auf loopback-Binding geändert, oder explizites Binding wurde für Sicherheitshärtung erforderlich

Warum Telegram weiterhin funktioniert

Der Telegram-Bot verwendet einen anderen Verbindungsmechanismus:

Telegram Bot Flow (not affected):
┌──────────────┐         ┌─────────────────────────────────────┐
│  Telegram    │         │  Container                           │
│  Servers     │ ──────  │  openclaw-gateway ── Webhook/Poll   │
│              │         │  (outbound connection, no inbound)   │
└──────────────┘         └─────────────────────────────────────┘

Dashboard Flow (broken):
┌──────────────┐         ┌─────────────────────────────────────┐
│  macOS       │         │  Container                           │
│  Chrome      │ ─X────  │  openclaw-gateway:127.0.0.1:18789   │
│  Browser     │ blocked │  (loopback-only inbound)             │
└──────────────┘         └─────────────────────────────────────┘

Telegram funktioniert, weil OpenClaw ausgehende Verbindungen zu den Telegram-Servern initiiert. Dashboard-Zugriff erfordert eingehende Verbindungen vom Browser zum Gateway.

🛠️ Schritt-für-Schritt-Lösung

Lösung 1: An alle Schnittstellen binden (Empfohlen)

Ändern Sie die Gateway-Binding-Konfiguration, um Verbindungen von der Docker-Virtuellen Schnittstelle zuzulassen.

Vorher (docker-compose.yml):

services:
  gateway:
    image: openclaw/gateway:latest
    environment:
      - OPENCLAW_GATEWAY_BIND=loopback  # Current setting
    ports:
      - "18789:18789"

Nachher (docker-compose.yml):

services:
  gateway:
    image: openclaw/gateway:latest
    environment:
      - OPENCLAW_GATEWAY_BIND=0.0.0.0   # Changed setting
    ports:
      - "18789:18789"

Änderungen anwenden:

$ docker compose down gateway
$ docker compose up -d gateway
$ sleep 2
$ curl -s http://127.0.0.1:18789/healthz
{"status":"ok","version":"3.9.0"}

Lösung 2: Dynamische Gateway-Adresse (Alternativ)

Verwenden Sie den Docker-Service-Namen beim Zugriff von innerhalb Docker Compose, und die Docker Desktop-Host-IP beim Zugriff vom macOS-Host.

# Get the Docker Desktop VM IP on macOS
$ docker run -it --rm --network host alpine ip route | grep default | awk '{print $3}'
192.168.65.0

# Or use the special host.docker.internal mapping
$ curl -s http://host.docker.internal:18789/healthz

docker-compose.yml mit host.docker.internal:

services:
  gateway:
    image: openclaw/gateway:latest
    environment:
      - OPENCLAW_GATEWAY_BIND=127.0.0.1  # Keep loopback for internal
    extra_hosts:
      - "host.docker.internal:host-gateway"

  cli:
    depends_on:
      - gateway
    environment:
      - OPENCLAW_GATEWAY_URL=http://host.docker.internal:18789

Lösung 3: Reverse Proxy Container (Production-Grade)

Deployen Sie einen nginx-Sidecar für ordnungsgemäßes Reverse Proxying mit zusätzlichen Sicherheitsvorteilen.

docker-compose.yml:

services:
  gateway:
    image: openclaw/gateway:latest
    environment:
      - OPENCLAW_GATEWAY_BIND=127.0.0.1  # Internal-only
    expose:
      - "18789"

  nginx:
    image: nginx:alpine
    ports:
      - "18789:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - gateway

  cli:
    depends_on:
      - gateway
    environment:
      - OPENCLAW_GATEWAY_URL=http://gateway:18789

nginx.conf:

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name _;

        # Proxy WebSocket connections for dashboard streaming
        location / {
            proxy_pass http://gateway:18789;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_read_timeout 86400;
        }
    }
}

Lösung 4: SSH-Tunnel (Maximale Sicherheit)

Für Umgebungen, in denen das Offenlegen eines Ports unerwünscht ist, verwenden Sie einen SSH-Tunnel.

# On macOS host, establish tunnel
$ ssh -L 18789:localhost:18789 docker-desktop-host

# Then in another terminal
$ open http://127.0.0.1:18789/

🧪 Verifizierung

Schritt 1: Gateway-Bind-Adresse bestätigen

# Check inside container
$ docker exec openclaw-gateway sh -c "ss -tlnp | grep 18789"
LISTEN  0  128  0.0.0.0:18789  0.0.0.0:*  users:(("openclaw-gateway",pid=1,fd=3))

# Should show 0.0.0.0, NOT 127.0.0.1

Schritt 2: HTTP-Endpoint-Antwort verifizieren

$ curl -s -w "\nHTTP Status: %{http_code}\n" http://127.0.0.1:18789/healthz
{"status":"ok","version":"3.9.0"}
HTTP Status: 200

Schritt 3: WebSocket-Konnektivität testen (Dashboard verwendet WebSocket)

# Install websocat if needed
$ brew install websocat

# Test WebSocket upgrade
$ websocat ws://127.0.0.1:18789/api/v1/stream
# Should establish connection and wait for events

Schritt 4: Bestätigen, dass Geräte-Pairing funktioniert

# In one terminal, watch for devices
$ docker compose run --rm openclaw-cli devices list
# Should show [] initially

# Open browser to http://127.0.0.1:18789/ and trigger pairing

# Re-check devices
$ docker compose run --rm openclaw-cli devices list
[
  {
    "id": "browser-xxxx",
    "type": "dashboard",
    "status": "pending",
    "created_at": "2025-01-15T10:30:00Z"
  }
]

Schritt 5: Vollständiger Dashboard-Flow-Test

# Get dashboard URL with token
$ docker compose run --rm openclaw-cli dashboard --no-open
Opening dashboard at: http://127.0.0.1:18789/?token=eyJhbGc...

# Open in browser (should load pairing screen)
$ open http://127.0.0.1:18789/

# Approve pending device
$ docker compose run --rm openclaw-cli devices approve browser-xxxx

# Refresh browser (should now show full dashboard)
$ open http://127.0.0.1:18789/

Schritt 6: Container-Health-Status verifizieren

$ docker compose ps
NAME                IMAGE                  STATUS
openclaw-gateway    openclaw/gateway       Up (healthy)

⚠️ Häufige Fehler

Fehler 1: Docker Desktop-Netzwerkadresse vergessen

# INCORRECT: Assumes 127.0.0.1 works
$ curl http://127.0.0.1:18789/healthz
curl: (52) Empty reply from server

# CORRECT: Use host.docker.internal on macOS
$ curl http://host.docker.internal:18789/healthz
{"status":"ok","version":"3.9.0"}

Fehler 2: Port-Veröffentlichung ohne Schnittstellen-Binding

Selbst mit -p 18789:18789, wenn OPENCLAW_GATEWAY_BIND=loopback gesetzt ist, blockiert Docker Desktop weiterhin die Verbindung, da der Container nur auf seinem internen Loopback lauscht.

Fehler 3: Firewall blockiert Docker Desktop

# macOS firewall may block Docker Desktop incoming connections
# Verify in System Preferences > Security & Privacy > Firewall

# Test with verbose curl to see connection status
$ curl -v http://host.docker.internal:18789/healthz

Fehler 4: Docker Desktop-Ressourcenbeschränkungen

# Check Docker Desktop resources
# Settings > Resources > Memory should be >= 4GB

# Container may be OOMKilled, check logs
$ docker compose logs gateway | grep -i memory

Fehler 5: Versionsspezifische Konfigurationsabweichung

Konfigurationsdateien von v2.9 sind möglicherweise nicht mit v3.9-Standards kompatibel.

# Check for deprecated environment variables
$ docker compose config | grep -i bind

# Compare with current defaults
$ docker exec openclaw-gateway env | grep OPENCLAW

Fehler 6: WebSocket-Proxy-Konfiguration in nginx

Bei Verwendung eines Reverse Proxys müssen WebSocket-Upgrades explizit konfiguriert werden, andernfalls hängt das Dashboard möglicherweise unbegrenzt.

# Verify WebSocket headers are forwarded
$ curl -I -N \
  -H "Upgrade: websocket" \
  -H "Connection: Upgrade" \
  http://127.0.0.1:18789/api/v1/stream

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

Fehler 7: Token-Ablauf während des Testens

Dashboard-Tokens können während der Entwicklungstests schnell ablaufen. Rufen Sie die URL immer vor dem Testen erneut ab.

# Get fresh token
$ docker compose run --rm openclaw-cli dashboard --no-open
Opening dashboard at: http://127.0.0.1:18789/?token=fresh_token_here

🔗 Zugehörige Fehler

Direkt zugehörige Fehler

  • curl: (52) Empty reply from server — Gateway an Loopback innerhalb des Containers gebunden, vom macOS-Host aus nicht erreichbar. Lösung: Setzen Sie OPENCLAW_GATEWAY_BIND=0.0.0.0.
  • ERR_CONNECTION_REFUSED — Browser kann die Docker Desktop-VM nicht erreichen. Überprüfen Sie die host.docker.internal-Auflösung oder ob Docker Desktop läuft.
  • ERR_CONNECTION_TIMED_OUT — Port nicht veröffentlicht oder Firewall blockiert. Überprüfen Sie Docker Desktop-Netzwerk und macOS-Firewall-Regeln.

Kontextuell zugehörige Fehler

  • upstream prematurely closed connection — nginx-Proxy-Fehlkonfiguration bei WebSocket. Stellen Sie sicher, dass proxy_read_timeout 86400 gesetzt ist.
  • 502 Bad Gateway — nginx kann Gateway-Container nicht erreichen. Überprüfen Sie Container-Netzwerk und depends_on-Konfiguration.
  • devices list gibt leeres Array zurück — Browser-WebSocket-Verbindung nie hergestellt. Überprüfen Sie Binding-Konfiguration und Browser-Konsole auf Verbindungsfehler.
  • Docker Desktop: connection refused to 127.0.0.1 — Bekannte Einschränkung bei macOS-Loopback und Docker Desktop. Verwenden Sie host.docker.internal oder 0.0.0.0-Binding.

Historische Probleme

  • GitHub Issue #2341 — "Gateway binds to loopback breaking macOS access" — Bestätigtes Docker Desktop-Netzwerkisolierungsproblem.
  • GitHub Issue #1892 — "Dashboard unreachable from Windows Docker Desktop" — Ähnliche Grundursache, Windows-spezifischer Netzwerk-Stack.
  • GitHub Issue #3107 — "Request: document macOS Docker Desktop networking requirements" — Dokumentationsanfrage für dieses spezifische Szenario.

Zugehörige Konfigurationsdokumentation

  • OPENCLAW_GATEWAY_BIND — Steuert die Netzwerkschnittstellen-Bindung. Werte: loopback, 0.0.0.0, oder spezifische IP-Adresse.
  • OPENCLAW_GATEWAY_URL — Überschreibt Gateway-Verbindungs-URL für CLI-Tools. Erforderlich bei Verwendung von nicht-Standard-Port oder host.docker.internal.
  • docker-compose ports vs exposeports veröffentlicht zum Host, expose macht Port nur für verknüpfte Dienste verfügbar.

Belege & Quellen

Diese Troubleshooting-Anleitung wurde automatisch von der FixClaw Intelligence Pipeline aus Community-Diskussionen synthetisiert.