Start with doctor
mb-local doctor checks every step of the path from your machine and names the
one that is broken. Run it before anything else.
--json for machine-readable output.
Getting to MobileBoost
The control plane is unreachable
api.mobileboost.io and relay-eu.mobileboost.io, or configure your corporate
proxy with --proxy-host.
A corporate proxy is in the way
mb-local reads HTTPS_PROXY and NO_PROXY, and on macOS it reads system
settings too. The egress path line tells you what it found.
If it found nothing and you know there is a proxy:
The proxy asks for a password
Kerberos and NTLM proxy authentication are not supported yet. If your proxy
requires either, ask for these hosts to be allowed without authentication, or
run the tunnel on a host that does not sit behind that proxy.
Your machine uses a PAC file
mobileboost.io, and pass it directly:
TLS inspection is rewriting certificates
The connection is refused even though the host is reachable
mb-local writes the request for you:
The usual resolution is a TLS inspection exception for the MobileBoost hosts.
Every major gateway supports one, and it is a routine request: Zscaler calls it
Do Not Inspect, Netskope SSL Do Not Decrypt, Palo Alto No Decrypt.
The generated document uses the right term for the product it found.
The clock is wrong
Reaching your own services
Requests are denied
Yourstatus output shows a growing denied count, and your test fails to reach
a host you expected to work.
*.acme.internal does not match acme.internal itself. Use
.acme.internal to cover both.
Rules with only IP ranges are another common cause. If you pass
--only-hosts '10.0.0.0/8' and your app requests api.internal by name, the
name matches no rule, so the request leaves the device normally instead of using
the tunnel. Add the hostname alongside the range:
The target is not reachable from your machine either
curl the service from the machine running mb-local, the tunnel cannot either.
The app ignores the proxy
Some HTTP clients do not use the device’s proxy settings. Flutter’sdart:io
HttpClient and Cronet-based stacks are the common ones. Requests from those
clients leave the device directly and never enter the tunnel, so they fail to
reach internal hosts while the rest of the app works.
For Flutter, point the client at the proxy explicitly in test builds:
The key is rejected
Exit code10 means the key is not recognised. Exit code 15 means the key is
valid but was issued without the tunnel:connect scope, so it cannot open
tunnels. The second one is fixed by an administrator, not by changing the secret.
localhost does not work
On a device, localhost is the device itself. Point the app at mb-local.net
instead, which resolves to 127.0.0.1 and is mapped back to loopback on the
machine running the tunnel.
If requests then return 400 Bad Request or a host header error, your framework
is rejecting the Host: mb-local.net header. Add mb-local.net to its allowed
hosts: ALLOWED_HOSTS in Django, config.hosts in Rails.
Some requests work and others do not
If the failing requests are all to the same service, check whether it uses HTTP/3 over QUIC. QUIC does not travel through the tunnel. Most mobile clients fall back to TCP automatically. If yours does not, disable HTTP/3 in test builds.Tunnel lifecycle
”A tunnel with this name is already connected”
Exit code13. Another process holds that name, usually a parallel CI job or a
tunnel left over from an earlier run.
- In CI, give every job a unique name. See Give every job a unique tunnel name.
- Locally, stop the other one with
mb-local --daemon stop --tunnel-name NAME, or replace it with--force.
The tunnel keeps reconnecting
A growingreconnects count in status means the connection is unstable.
mb-local reconnects automatically and new requests keep working, but requests
already in flight fail when a reconnect happens.
Common causes are laptop sleep, Wi-Fi switching, and a VPN that drops. For
anything long-running, use a shared tunnel
on a stable host.
A run fails immediately with a tunnel error
If the named tunnel is not connected when a run starts, the run fails right away instead of starting and timing out on every request. Check that the tunnel is up withmb-local status, and that the name in your payload matches exactly.

