---
title: "Build Host"
description: "Controls whether a device will run image builds submitted by other people with wendy run --build-host."
---

Controls whether a device will run image builds submitted by other people with
[`wendy run --build-host`](../run.md#remote-build-host).

For a complete example that sends one build to several devices, follow [Build Once, Deploy to Several Devices](../../../../../../guides/fleet-deployment/).

```sh
wendy device build-host enable  --device spark-office
wendy device build-host status  --device spark-office
wendy device build-host disable --device spark-office
```

## The role is off by default

A remote build executes the caller's build instructions on the host. Enabling
the builder role allows users in your organisation to run builds there. Use a
host intended for shared builds.

The role requires an explicit `enable` command. Installing the agent,
provisioning the device, or installing BuildKit does not enable it.

Enabling takes effect on the next build. There is no agent restart.

## Who may change it

`enable`, `disable`, and build submission require a **user** certificate.
Device certificates are rejected, including on hosts with the builder role
enabled. The unauthenticated port the agent serves before provisioning does not
accept builds.

The one caller without a certificate is an on-device container holding the
**admin entitlement**, which reaches the agent over a local unix socket. There
the socket's own permissions are the credential, and that entitlement already
carries full agent authority.

Cross-organisation callers are rejected before any of this is reached, by the
agent's mTLS organisation check.

## Source contexts and concurrent builds

The host stores source files under `/var/lib/wendy/buildctx/<id>` between
builds so BuildKit can reuse its local-source cache. `<id>` is the app ID for a
single-image app, and `<app>-<service>` for each service of a
[multi-service app](../../../../apps/wendy-services.md#remote-build-host). The
files are cleared and rewritten at the start of each build with that id, but
are not deleted after the build finishes.

Builds with the same id are serialised on a host. Builds with different ids can
run concurrently, so the services of one app build side by side and each keeps
its own cache between runs. This prevents one build from replacing source files
that another build with the same id is still compiling.

## Delivery

The finished image reaches the target by chunks: the build host asks the device
which layers and chunks it already holds and sends only the missing bytes into
its content store. A link that drops mid-transfer resumes from the chunks the
device already staged rather than restarting. This leg is scoped to one build —
the host dials the target's agent with its own certificate, pinned to that one
device, so a build for one target cannot deliver to another.

A device whose agent predates chunked delivery receives a registry push instead,
as does a build run with `--chunking=off`. The build log says which route it took.

## Delivery credentials

On the registry-push route, the agent exposes a loopback endpoint for BuildKit to
push through while a build runs. The endpoint holds the credentials for reaching
the target device and requires a password generated for that build alone. Other
processes on the host cannot use it to push their own image to the device.

The password is passed to BuildKit in a file with mode `0600`. It is not placed
on the command line, where other local users could read it through `/proc`.

## `status`

`status` reports what a build host can actually do, which is worth checking
before pointing a build at it:

```
Builder role: enabled
BuildKit:     available (v0.32.2)
Cache:        /data/buildkit/state (823.3 GiB free of 911.7 GiB)
Platform:     linux/arm64
Builds:       linux/arm64 natively
```

- **Builder role** — whether `enable` has been run.
- **BuildKit** — whether a buildkitd socket is present at
  `/run/buildkit/buildkitd.sock`, and the `buildctl` version found on `PATH`.
  BuildKit is installed out-of-band (distro package, release tarball, or not at
  all), so "which version is on that box" is otherwise invisible from the CLI.
- **Cache** — BuildKit's state directory and the free and total space on its
  filesystem. If it reports `inspection failed`, remote builds are refused
  until the cache location and available space can be determined.
- **Builds** — the platforms this host builds natively. Only its own platform is
  claimed. Emulated platforms are reported empty rather than guessed at, so a
  cross-architecture build is refused up front instead of silently running under
  QEMU and taking twenty minutes.

`status` needs neither the builder role nor BuildKit to answer — that is the
point of it. A device that is missing both still reports so.

## Cache space safety

Before starting a remote build, `wendy run --build-host` checks the free space
where BuildKit keeps its cache:

- Less than 8 GiB free: the build is refused.
- At least 8 GiB but less than 25 GiB free: the build proceeds with a warning.
- At least 25 GiB free: the build proceeds without a cache-space notice.

To move the cache, set buildkitd's `--root` option or put a top-level `root` in
the TOML file selected by its service, then restart buildkitd. For example:

```toml
root = "/data/buildkit/state"
```

The active file is not necessarily `/etc/buildkit/buildkitd.toml`: a service can
select another one with `buildkitd --config`. After restarting, run
`wendy device build-host status` and confirm that the `Cache` line names the
intended path and filesystem. If it reports `inspection failed`, inspect the
running buildkitd command and its active configuration rather than assuming the
default path.

## A Mac cannot be a build host

The Mac agent runs Linux containers through Apple Container, which has no
BuildKit underneath, so `status` on a Mac reports BuildKit unavailable and
`enable` cannot make it otherwise. A Mac remains a perfectly good build
*target*, and the intended machine to run `wendy run` from.

## Adopted Linux hosts

A non-WendyOS Linux machine that has been adopted into the mesh — a DGX Spark
running Ubuntu, say — is a legitimate build host, but Ubuntu ships no `buildkit`
package. Install the upstream release tarball and make sure `buildctl` is on
`PATH` by name (the agent runs it unqualified, and systemd units get a minimal
`PATH`, so a symlink into `/usr/bin` is the reliable placement). `status` then
reports the version it finds. If the host's root filesystem is small, configure
BuildKit's state directory on a larger filesystem before submitting builds.
