Bazel iOS builds on Limrun RBE
Build Bazel Apple projects on Limrun's remote Mac workers — from any environment
(Linux, Windows, macOS, VM, container), no local Xcode.
brings up
a remote RBE stack, tunnels it to a local port, and writes a
config so
bazelisk build --config=limrun
runs Apple actions remotely. Never fall back to
local Xcode or build tools.
Auth and CLI
Install if needed:
. Auth is
or
(may be set outside the project — don't ask for it just because
it's absent). The CLI is the source of truth: the commands in this skill are
verified, but if a flag errors or you need one not shown here, check
instead of guessing.
Build
- From the Bazel workspace root (has / ), run
. It sets up the instance + config and prints the
exact build command. The tunnel runs in the background (prints a PID);
keeps it foreground.
- Run the printed command, e.g.
bazelisk --digest_function=sha256 build --config=limrun //App
.
Don't hand-write
or the flags — the CLI generates them for the fleet's
Xcode and your OS. Re-run
(after
) to refresh after a
fleet Xcode upgrade.
To add your own Bazel flags to the limrun path without editing the generated
config, put them in
at the workspace root. The
generated config try-imports it last, so your
lines win, and
it survives
regeneration (
does not).
Run on a simulator
is build-only; attach a simulator when the user wants to see or
run the app. Check or attach (it installs the last build immediately, so no
rebuild is needed):
bash
lim xcode get # is a simulator already attached?
lim ios create --attach # attach one
If the attach output includes a signed stream URL, share it with the user as a
Markdown link, such as Live simulator.
With a simulator attached, every successful
build automatically
reinstalls and relaunches the app, no separate install step:
bash
bazelisk --digest_function=sha256 build --config=limrun //App
Notes:
- Attach upfront if you already know you want a sim:
(attaches at startup, removed on ).
- Auto-install happens server-side from the build's events; there is no
subcommand or flag. To force a reinstall,
rebuild (a cache-hit rebuild is seconds).
- It fires when the successful invocation produced a single app, so in a
multi-app workspace build one app target per invocation (, not
); a multi-app build succeeds but installs nothing.
To tap, type, read the element tree, screenshot, or record the running app,
switch to the
skill.
Upload builds as assets
To publish a build as a Limrun asset (preview links, installing on other
simulators, CI artifacts), arm uploads at tunnel start or upload one build
after the fact:
bash
lim xcode rbe --auto-upload preview/my-app --upload-ttl 24h # every successful build refreshes the asset
lim xcode rbe upload preview/my-app --ttl 24h # one-shot: the newest successful build
- holds for the tunnel's lifetime: each successful
build re-uploads the app under that asset name, no
post-build step. Upload results land in .
- runs from the workspace root and needs a background tunnel
plus at least one successful build; it errors otherwise.
- TTLs are Go durations (, ; is invalid) and optional.
- To change the config of a running tunnel, and
re-run; the CLI refuses a mismatched re-arm instead of silently ignoring it.
- Preview an uploaded app in a browser at
https://console.limrun.com/preview?asset=<name>&platform=ios
.
Teardown
Stop with
(~20s to tear the remote stack down) and delete the instance with
Gotchas
- Always pass before (use the command the
CLI prints verbatim). The Limrun cache is SHA256-only; Bazel 9 defaults to
BLAKE3. It's a startup flag, so it can't live in . Symptoms:
build →
Cannot use hash function BLAKE3 with remote cache
; install →
non-SHA256 digest … rebuild with --digest_function=sha256
.
- Run from the workspace root, not a subdirectory — it writes
there and fails fast otherwise.
- A green build doesn't prove remote execution — cache hits ( / ) make builds pass even with the tunnel gone. To force
and verify real remote execution, see
references/verify-remote.md
.
- The printed path won't exist on your machine — the build command
carries
--remote_download_outputs=minimal
, which keeps the artifact in the
instance's cache and downloads nothing. Bazel still prints its usual
Target //App:App up-to-date: …/App.ipa
line, but that file is not on
disk. This is expected, not a failed build. With a simulator attached, the
build auto-installs from the artifact's cache digest (read from the build event
log, not the local file). Only if you genuinely need the locally, drop
--remote_download_outputs=minimal
from the build command and Bazel downloads
the top-level output; auto-install keeps working either way.
- A fresh instance can fail the first build with
Lost inputs no longer available remotely
(e.g. ). It's a transient cache eviction
between instances, not a code error; Bazel prints Found transient remote cache error, retrying the build...
and the retry succeeds. To avoid hitting it
mid-demo, pre-warm with a full build right after .
You don't have permission to save … in "CoreSimulator"
(actool/ibtool) is
a fleet-side device gap, not your config. Retry; if it persists, report it to
Limrun.
- The project's own Bazel settings can fight RBE (Xcode pinned via a Starlark
transition, custom
remote_default_exec_properties
, sandbox-hostile genrules).
These are per-project, not Limrun bugs — walk
references/project-compatibility.md
before concluding RBE is broken.