Skip to main content
A CI job can open a tunnel, run tests against it, and close it again. The one thing to get right first is where the tunnel runs.

Pick the right runner

The tunnel has to run somewhere that can already reach your internal services.
Starting mb-local on a cloud-hosted runner does not fail loudly. The tunnel connects, and then every request through it fails because the runner cannot reach your services either. If your runners are not inside your network, use a shared tunnel.

GitHub Actions

The action handles three things that are easy to get wrong by hand:
  • It picks a unique tunnel name per job, derived from the run ID, the attempt number, and the runner. Two jobs never collide.
  • It waits until the tunnel is usable before the step finishes, so your tests never start against a tunnel that is not up yet.
  • It stops the tunnel in a post step, which runs even when the job fails or is cancelled.
It exports the name it chose as MOBILEBOOST_TUNNEL_NAME for later steps.

Inputs

Other CI providers

Without a ready-made action, do the same three things by hand: unique name, wait for ready, and stop it in a step that always runs.
Add MOBILEBOOST_API_KEY as a masked project variable under Settings > CI/CD > Variables. It is then already in the environment, so nothing needs to pass it explicitly.after_script runs even when script fails, which is what makes the cleanup reliable. $CI_JOB_ID is unique per job, so parallel jobs never collide.

Give every job a unique tunnel name

A tunnel name identifies one connection. If two jobs use the same name at the same time, the second one is refused rather than silently taking over the first. Build the name from something your CI guarantees is unique: If you omit --tunnel-name, mb-local derives one from these variables itself.
A matrix build needs the matrix index too. The run ID alone is shared by every job in the matrix, so five parallel jobs would fight over one name and four of them would be refused.

Wait for the tunnel, do not sleep

--wait-for-ready blocks until the tunnel can actually carry traffic, then keeps running. Use it instead of sleep:
If the tunnel is not usable within the timeout, the command exits non-zero and your job fails with a clear reason. That is much better than a run where every test times out.

Run a shared tunnel

For teams running many jobs, one long-lived tunnel beats one tunnel per job. It removes tunnel setup from every pipeline, works for cloud-hosted runners, and removes name collisions entirely. Run it on any host inside your network: a small VM, a container in the cluster that already hosts staging, or an existing build server.
Jobs then reference it by name and start nothing themselves:
Raise --links on a shared tunnel. It sets how many parallel connections carry traffic, so a large test run does not queue behind itself. Eight is a good starting point for a tunnel serving a whole team.
Keep exactly one process per shared tunnel name. Two replicas would both claim the name, and the second is refused.

Keep the access key out of your logs

The access key is a credential. Store it in your CI secret store and pass it through the environment rather than --key, so it never appears in a command line that gets logged.
mb-local never writes the key to its own logs at any verbosity.

Check the exit code

CI should tell a wrong key apart from a network blip. mb-local uses distinct exit codes so you can: Full list in the CLI reference.