Configuring Bazel for Conveyor¶
Add to .bazelrc (workspace or ~/.bazelrc). Conveyor speaks the standard Build Event
Service protocol, so nothing is installed on the client.
# Where builds go and where the link in the terminal points
build --bes_backend=grpcs://conveyor.example.com:1985
build --bes_results_url=https://conveyor.example.com/invocation/
# Authentication: an API key created in Settings (or `mix run` on the server)
build --bes_header=x-api-key=conveyor_xxxxxxxx_...
# Tags you can filter and chart by (lowercased; reserved keys get a user. prefix)
build --build_metadata=TEAM=infra
build:ci --build_metadata=CI=true --build_metadata=BRANCH=$BRANCH_NAME
# Survive a rolling restart of the server (Bazel's default retry budget is seconds)
build --build_event_upload_max_retries=10
# Only test logs and the profile are uploaded to the remote cache; outputs stay local
build --remote_build_event_upload=minimal
Does streaming slow the build down?¶
No. Bazel sends events on a background thread while actions run. What can cost time is
the end of the command, controlled by --bes_upload_mode:
| Mode | Behaviour | Use |
|---|---|---|
wait_for_upload_complete (default) |
The command returns once every event is acknowledged and files are uploaded. Against a healthy Conveyor the wait is the last ack, a few hundred milliseconds. | CI: the job's success means the results are stored. Set --bes_timeout=120s; the default is no timeout, so an unreachable server would hang the exit. |
nowait_for_upload_complete |
The command returns immediately; the persistent Bazel server finishes the upload. The next invocation waits for it first. | Developers' machines. bazel shutdown right after a build can drop an upload still in flight. |
fully_async |
As above, and the next invocation does not wait either. Upload failures never affect an exit code. | Only when the results are best-effort. |
build:ci --bes_upload_mode=wait_for_upload_complete --bes_timeout=120s
build:dev --bes_upload_mode=nowait_for_upload_complete --bes_timeout=30s
Event volume is what costs the server and the network: --build_event_publish_all_actions
emits one event per action and multiplies the stream for large builds; leave it off unless
you want every action in the Actions tab.
Profiles, test logs and artifacts¶
With --remote_cache set, Bazel uploads the build profile (command.profile.gz) and each
test's test.log / test.xml to the cache and references them in the event stream.
Conveyor fetches them from the cache endpoint configured in Settings and renders the
timeline and test outputs.
Without a remote cache, run with --remote_cache=grpcs://conveyor.example.com:1985
(Conveyor's built-in CAS sink, CAS_SINK_ENABLED=true) together with
--remote_upload_local_results=false --noremote_accept_cached, so Bazel uploads only the
referenced files and never treats Conveyor as a cache for outputs. Or upload a profile
after the fact with tools/bes-upload-profile.
Why did it rebuild? (execution log)¶
Bazel's compact execution log records every spawn with the digest of each input and
output, its runner, cache status and timings. Conveyor diffs it against the previous build
of the same project (same branch tag when the build has one) and shows, per spawn, the
inputs that changed, the spawns that ran again with identical inputs (cache misses or
non-hermetic actions — "outputs differ" flags the latter), and new actions.
build --execution_log_compact_file=/tmp/exec.log.zst
After the build, upload the file to the invocation (an upload-scoped key):
tools/bes-upload-profile --server https://conveyor.example.com --key conveyor_... \
--invocation <uuid> --execution-log /tmp/exec.log.zst
# or: curl -X PUT -H "x-api-key: conveyor_..." --data-binary @/tmp/exec.log.zst \
# https://conveyor.example.com/api/v1/invocations/<uuid>/artifacts/execution.log.zst
Any artifact name containing execution.log, exec_log or execlog is parsed. The log
only lists spawns that ran or were served from the remote cache; actions skipped by
Bazel's local action cache do not appear, so a fully incremental build has an empty log.
Versions¶
Conveyor's protocol definitions come from Bazel 9.2 and are backwards compatible; builds
from Bazel 7 and 8 are exercised in CI (.github/workflows/e2e.yml).