On Aspect Workflows runners
The runner is inside the deployment. Its environment already carries the cache, build event service and results URL, and it holds its own credential, so nothing in your pipeline names an endpoint or handles a token for the build itself. Aspect CLI tasks need nothing.aspect build and aspect test build against the runner’s services without being asked. They refuse --remote: the flag names a configured deployment, which would route the build off the runner’s own services.
Vanilla bazel needs an rc, which the setup step writes.
Commands that don’t call Bazel need nothing. Scripts, Docker builds and other tools run on a Workflows runner as on any other; target the runner group and skip the setup step.
The runner image puts aspect and bazel on PATH: aspect is the Aspect CLI launcher, and bazel is the legacy Aspect CLI unless the deployment is configured to install upstream Bazelisk as bazel instead. A job can tell the two apart by ASPECT_WORKFLOWS_RUNNER_NO_LEGACY_CLI=1, which is set only in the Bazelisk case.
The setup step
Recommended for every job that callsbazel directly. Add your CI host’s setup integration before the first bazel call. On a Workflows runner it:
- Runs
aspect ci runner-health-check, which confirms the runner is ready for the job. - Signs in, when given an Aspect API token. Tasks that post to pull requests (status checks, lint comments, suggested fixes) reach the GitHub or GitLab App with it; the build itself doesn’t need it. Pass it as
aspect-api-tokenon GitHub Actions, or asASPECT_API_TOKENin the environment on the others. - Runs
aspect setup bazelrc, which writes~/.aspect/bazelrc, imported from~/.bazelrc. It names the runner’s remote cache and build event service and puts Bazel’s output base on the runner’s NVMe, and it’s what a vanillabazelcall picks up.
bazel come with the runner image, so there’s nothing to install. Your CI host’s pipeline page has a complete pipeline.
- GitHub Actions
- Buildkite
- CircleCI
- GitLab CI
before_script replaces the template’s, and the setup doesn’t run. List the template’s first, with !reference:
GitHub Actions
aspect auth login line if the job doesn’t post to pull requests.
Runner groups covers which group a job lands on.
What the rc sets
The rc pointsbazel at the runner’s remote cache and build event service and puts the output base on the runner’s NVMe. Where the runner’s cache doesn’t accept compressed blobs, it sets --noremote_cache_compression, because Bazel errors when a repository asks for compression from a cache that lacks it. Remote execution is offered but not switched on.
Each flag the rc sets can be dropped with --omit-bazel-flag, except endpoints, credentials, the --config chains and the runner’s paths. --bes_backend and --bes_results_url can be dropped, so a repository that streams build events somewhere else keeps doing that. See aspect setup bazelrc.
Where each integration lives
The versions above were current when this page was written. Each publishes on its own schedule, so take the latest from its own listing:
Buildkite’s shorthand resolves
aspect-build/setup-aspect to the full repository name, which is why the plugin reference looks shorter than the repository it comes from.
The GitHub and Buildkite examples pin a commit; the CircleCI and GitLab ones pin a
version. An Action reference and a Buildkite plugin reference are git refs, so a commit
is the tighter pin. A CircleCI orb resolves from the orb registry and a GitLab component
from the CI/CD Catalog, where an immutable published version is the pin. The trailing
comment gives the version each commit corresponds to.
Tuning the setup step
The integrations take the same inputs for the rc. Each is passed toaspect setup bazelrc, and an unset input leaves that flag off the command line, so the CLI applies its own default:
Each CI host passes them its own way:
bazelrc inputs, since the runner image and its rc supply those.
On runners you manage
A runner you manage needs the launcher, a credential for the deployment, and an rc naming the deployment’s endpoints. The setup integrations don’t cover it: they take no deployment host and read onlyASPECT_API_TOKEN, so run the steps below in the job.
Which hostname depends on where the runner sits. Inside the deployment’s VPC, or a network peered with it, it reaches the private endpoint; anywhere else, the external one. The setup is the same either way, and your Aspect contact or your Terraform outputs say which host applies.
On a persistent runner, pass
—force to aspect setup bazelrc. The home directory
outlives the job, so an rc written by the first job on that machine is still there for the
next, and the command keeps an existing rc rather than overwriting it. An ephemeral runner
starts clean, so this changes nothing there.Record the deployment, then log in
The same path a developer takes, so the CI job and a laptop agree on what the deployment is called. The CLI recognizes GitHub Actions, Buildkite, CircleCI and GitLab CI by their own variables, and treats any other host as CI whenCI is set. Jenkins and TeamCity don’t set it, so the snippet does; without it, aspect setup bazelrc would treat the job as a developer’s machine and turn nothing on. Set it in the job’s environment too, so later aspect tasks treat the job as CI.
sudo, so the job needs it. The launcher version is separate from the CLI version, which .aspect/version.axl pins.
The API token is per deployment: one minted for one deployment isn’t accepted by any other, or by Aspect Cloud. Your Aspect contact or the deployment’s admin issues it. Store it as a CI secret named ASPECT_API_TOKEN_<NAME>, the deployment name uppercased with every non-alphanumeric character replaced by _, which is the variable the CLI reads for that deployment. A deployment named gcp.acme reads ASPECT_API_TOKEN_GCP_ACME.
--with-api-token exchanges the token for a short-lived session and stores it where a developer’s login goes: the OS keyring when the CLI can open one (on Linux, the kernel keyring), otherwise a 0600 file at ~/.aspect/credentials.json. Later steps in the same job reuse it. Set ASPECT_CREDENTIALS_FILE to store it in a file at that path instead.
Or name the endpoints outright
Set the endpoints in the job’s environment and the CLI takes them from there, with noauth configure step and nothing written to ~/.aspect/config.json:
Your Aspect contact gives you these; on a self-hosted deployment they’re Terraform outputs.
auth configurewhen the job also runs Aspect CLI tasks, or you want the deployment named the same way everywhere.- Environment variables when the endpoints need no Aspect credential from the job, or authenticate through a credential helper you supply, and you’d rather the pipeline say what it points at than depend on state in a home directory.
If the deployment uses your own identity provider
There’s no Aspect API token to issue, because Aspect doesn’t operate the directory that would issue it. CI authenticates with a JWT from your identity provider, fetched by a Bazel credential helper you supply: a client-credentials grant, a workload identity federation exchange, or whatever your organization already uses for machine-to-machine auth. Point Bazel at it with--credential_helper. One helper serves the cache, build event and remote execution endpoints.
Label your builds
A vanillabazel call reaches the build event service with no commit, branch or pull request attached, so it arrives in the UI anonymous. aspect setup workspace-data supplies them as Bazel’s workspace status command:
--workspace_status_command, so a repository that already has a status script calls the task from inside it. See composing with an existing script.
--remote on CI
You rarely pass it. A task’s --remote defaults to auto, which means bare --remote on CI, and nothing off it. On a runner you manage, a task reaches the default deployment’s cache and build events without the flag.
auto steps aside wherever an explicit flag would fail:
Bazel treats a credential helper that can’t produce a token as fatal, which is why
auto won’t wire an endpoint it can’t authenticate to.
Remote execution
Never turned on for you: a deployment may run an executor while most jobs still want their actions local. How a job asks for it depends on how the job names the deployment:
Which worker pool an action lands on is decided by its Bazel platform; see Targeting remote execution worker pools.
Related
- Local setup: the same deployment from a developer’s machine.
- : connect Workflows runners to your CI provider.
- Build metadata: label vanilla
bazelbuilds with their commit and branch.

