> ## Documentation Index
> Fetch the complete documentation index at: https://docs-stg.outerlayer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Run work on your own machines

> outerlayer runner takes work from your factory's queue and runs it on a machine you control.

A host is a machine you control. The runner, `outerlayer runner`, is the
process on that host that builds your factory's items. Together they are a
self-hosted runner: the platform stays at app.outerlayer.ai, and each build
and its code stay on your machine. Self-hosting the platform itself is a
different thing, and is [coming soon](/self-host).

<Tip>
  Setting up your first host? Start at [Set up your first host](/set-up-a-host).
  This page explains what a runner does with each build.
</Tip>

The runner takes items from two queues:

* **`implement`** builds an item from its issue.
* **`amend`** answers the review threads on an item's open pull request.

For each item, the runner sets up a place to work, runs your agent, and
cleans up.

You decide two things on the host: how each build is isolated (the hooks)
and which command starts your agent for each queue. With the built-in
hooks, you write no script at all.

## Choose how to run the runner

Find your machine in the first column. The last column names the page that
sets it up. "Builds get" is the isolation each build records.

| Machine | How the runner runs | Builds get | Starts at | Updates itself | Page |
| - | - | - | - | - | - |
| x86\_64 Linux whose runner account can open `/dev/kvm` | A systemd service | `vm`: a microVM each | Boot | Yes | [Set up your first host](/set-up-a-host) |
| Linux with Docker Engine 26 or later | A systemd service, or the runner image | `container` | Boot | The service does. The runner image does not: you pull a new one. | [Set up your first host](/set-up-a-host) or [Run the runner in a container](/run-the-runner-in-a-container) |
| Mac with Docker Desktop | The runner image on Docker Desktop | `container` | When Docker Desktop starts | No: you pull a new image | [Run the runner in a container](/run-the-runner-in-a-container) |
| Mac with no Docker | A service in a Lima VM, which `runner init --vm` makes | `container` | Login | Yes | [Run builds on macOS](/run-builds-on-macos) |
| Windows with Docker Engine inside WSL2 | A service in WSL2 | `container` | Login | Yes | [Run builds on Windows](/run-builds-on-windows) |
| Windows with Docker Desktop only | The runner image on Docker Desktop. Nobody has verified it end to end. | `container` | When Docker Desktop starts | No: you pull a new image | [Run the runner in a container](/run-the-runner-in-a-container#docker-desktop) |
| Any other machine | A service or a terminal, with `--allow-process-builds`. On a Mac, `--no-vm`. | `shared-user`: no isolation | Boot under a service, else when you start it | Only under a service | [The process fallback](/write-your-own-hooks#the-process-fallback) |

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/outerlayer-stg/vtkewxrpx_NzS0BQ/images/runner-routes-light.svg?fit=max&auto=format&n=vtkewxrpx_NzS0BQ&q=85&s=3a72c0836e3c4ee7a78d4eee79475829" alt="Four ways to run the runner. On Linux, systemd starts the runner service at boot, and it runs builds in containers or microVMs. On a Mac with Docker Desktop, Docker's restart policy keeps the runner image's container running in the Docker Desktop VM, and builds run as containers beside it. On a Mac without Docker, a launch agent starts a Lima VM at login, where a systemd service runs the runner and builds run in containers on Docker Engine. On Windows, a scheduled task starts a WSL2 distribution at login, where a systemd service runs the runner and builds run in containers on Docker Engine. Linux can also run the runner image; microVM builds need the service." width="660" height="454" data-path="images/runner-routes-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/outerlayer-stg/vtkewxrpx_NzS0BQ/images/runner-routes-dark.svg?fit=max&auto=format&n=vtkewxrpx_NzS0BQ&q=85&s=6383e8a5cd5de6fe15c6cf5fea36668a" alt="Four ways to run the runner. On Linux, systemd starts the runner service at boot, and it runs builds in containers or microVMs. On a Mac with Docker Desktop, Docker's restart policy keeps the runner image's container running in the Docker Desktop VM, and builds run as containers beside it. On a Mac without Docker, a launch agent starts a Lima VM at login, where a systemd service runs the runner and builds run in containers on Docker Engine. On Windows, a scheduled task starts a WSL2 distribution at login, where a systemd service runs the runner and builds run in containers on Docker Engine. Linux can also run the runner image; microVM builds need the service." width="660" height="454" data-path="images/runner-routes-dark.svg" />
</Frame>

Every row with Docker also needs Docker's Buildx plugin, version 0.18 or
later. The runner image cannot run microVM builds. A runner installed on a
Mac, or in WSL2, does not use Docker Desktop.

The hooks in the runner's config decide what a build can reach on the host.
`outerlayer runner init` picks the strongest isolation the machine
supports. The full rule is in
[`outerlayer runner init`](/reference/cli-runner#outerlayer-runner-init).

* **`builtin:vm`** runs each build in a Firecracker microVM with its own
  kernel and no network device. It can run Docker when its recipe asks for
  it.
* **`builtin:container`** runs each build in a container with no host files
  and no network but loopback. It shares the host's kernel.
* **`builtin:process`** isolates nothing. The build runs as the runner's
  account and can read that account's files.
* **Your own hooks** isolate whatever your provision hook sets up. See
  [Write your own hooks](/write-your-own-hooks).

What each kind protects is in [Security and limits](/build-security).

## What happens to one build

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/outerlayer-stg/M2z6K_ww2sAtqE1w/images/build-stages-light.svg?fit=max&auto=format&n=M2z6K_ww2sAtqE1w&q=85&s=f794a4eb2858d82aad3b17a812b8896b" alt="One build, top to bottom: Claim, Provision, Command, Judge, Host checks, Sync, Cleanup, Verdict, Report and Release, each labelled with who runs it. The judge runs only for a judge check. A command that exits non-zero skips the judge, the host checks and the verdict, and is released failed. The verdict decides between ok and incomplete." width="660" height="810" data-path="images/build-stages-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/outerlayer-stg/M2z6K_ww2sAtqE1w/images/build-stages-dark.svg?fit=max&auto=format&n=M2z6K_ww2sAtqE1w&q=85&s=127bf286c8f5046df7a85aa326e60396" alt="One build, top to bottom: Claim, Provision, Command, Judge, Host checks, Sync, Cleanup, Verdict, Report and Release, each labelled with who runs it. The judge runs only for a judge check. A command that exits non-zero skips the judge, the host checks and the verdict, and is released failed. The verdict decides between ok and incomplete." width="660" height="810" data-path="images/build-stages-dark.svg" />
</Frame>

1. **Claim.** The runner claims an item someone queued with
   `outerlayer work build` or **Build** on the Work page. An item added with
   `outerlayer work build --local` or **Build locally** is never queued for a
   runner. If a report
   hook is set, it runs now with `OUTERLAYER_PHASE=start`.
2. **Provision.** The provision hook makes the working directory, such as a
   clone inside a container of its own.
3. **Command.** Your command starts the agent in that directory. The runner
   renews its claim while the build runs. A claim it stops renewing expires
   after fifteen minutes.
4. **Judge.** When the command exits 0 and the repository has a
   [judge check](/write-a-validator#a-judge-on-your-host), the runner
   starts a fresh agent to judge the criteria. See
   [The judge stage](/reference/build-reference#the-judge-stage).
5. **Host checks.** When the command exits 0, the runner runs the
   repository's [host checks](/reference/build-reference#host-checks) and
   records their results.
6. **Sync.** The runner runs `outerlayer sync`, so the build's sessions
   upload.
7. **Cleanup.** The cleanup hook runs, however the command ended.
8. **Verdict.** When the command exited 0, the runner reads the item's
   checks to decide between `ok` and `incomplete`. See
   [When the command exits 0](#when-the-command-exits-0).
9. **Report.** If a report hook is set, it runs again with
   `OUTERLAYER_PHASE=end` and the outcome the release carries.
10. **Release.** The runner releases the claim with the outcome.

An item another claim holds, such as a person's own session, is refused.
Linked sessions and pull requests never stop a build by themselves.

### When the command exits 0

An exit of 0 says only that the agent's session ended normally. So after the
checks, the sync and the cleanup, the runner reads the item's evidence
verdict, the `evaluation` that `GET /v1/work-items/{workItemId}` returns. It reads it before the report hook, so that hook sees the outcome
the release carries.

* **The item has a pull request and passes**, or waits only on a person's
  review: the release is `ok`.
* **The item has no pull request**: a build that opens no pull request is
  released `incomplete`, saying so, whatever the checks say. A runner build
  opens its pull request with `outerlayer work open-pr`.
* **A check failed or has no result**: the release is `incomplete`. Its
  reason names each such check and marks the ones with no result, for
  example `checks not passing: Migration must run: Migrations ran against a local database (no result)`.
  While the build held the claim, a required check with nothing recorded
  was only waiting. The build has ended, so the runner counts it as missing.
* **The item has no verdict yet**, or still waits on session links to be
  confirmed: the release is `incomplete`, and its reason says so.
* **The read fails**: the release is `incomplete`, and its reason says the
  item's status could not be read.

The verdict is computed again after the build's sessions and checks arrive,
so it can lag them. A verdict that is not passing is read again every 30
seconds, for up to 5 minutes. The runner decides on the last read. A
passing verdict is believed at once.

`incomplete` uses up the build request, as `ok` does. No runner takes the
item again until a person asks for another build. To ask, run
`outerlayer work build --item <n>`, or select **Rerun on a host** in
the item page's **Last build** block. When it is offered is in
[Rerun a Stuck item](/review-the-work#rerun-a-stuck-item).

With an older gateway or runner, the verdict can be skipped; see
[Mixed versions](/reference/build-reference#mixed-versions).

## Which items a runner takes

A runner polls for work and takes one item for each free slot. It reads the
queues in a fixed order:

1. The amend queue, if the runner takes `amend`. This is work on a pull
   request that a person reviewed or that has a merge conflict.
2. The implement queue, if the runner takes `implement`.

The runner takes amend work before implement work, whenever both wait.

Each queue is oldest request first. The implement queue orders items by the
time of their oldest unused build request. The amend queue orders items by
the time of their amend trigger. Items with the same time come in `id`
order.

In the implement queue, a change to an item, such as a comment or a new pull
request commit, does not move it. A second build request on an item that
already waits does not move it either. An item whose build ended
`interrupted` or `lease_lost` keeps its place.

In the amend queue, a new review failure or a new build request sets a new
trigger time. The item then moves back in the queue.

The gateway lists a queue in pages of 100 items. A runner with `repos.include`
or `repos.exclude` rules reads the next page when it claims none of the items
on the page. This happens when no item is for a repository its rules allow,
or when other runners claimed every allowed item first. It stops reading when
it claims an item or the pages run out. The runner log says how many listed items its rules skipped.

## Give a build instructions

A person starting a build often knows something the issue does not say.
Instructions are text for the agent that runs that one build. It reads
them as guidance from the person who asked. They never change which issue is
built or which checks run, and the issue's criteria win if the two disagree.
The agent names any conflict, and what it did about it, in the pull request
description.

You can give instructions, up to 2000 characters, in three places:

* **Add work.** Select **Build** on an issue. A step opens with an
  **Instructions for the agent** field. **Build locally** has no field,
  because you build that item in your own session.
* **Rerun.** Select **Rerun on a host** in the item page's **Last build**
  block. A dialog opens with an empty field, and the rerun starts when you
  select **Rerun**. When the item has an open pull request, the rerun is an
  amend, and the dialog says it does not use instructions.
* **CLI.** Pass `--instructions <text>` to `outerlayer work build`. See the
  [CLI reference](/reference/cli-work#outerlayer-work-build).

Instructions belong to one build request. A rerun without instructions gives
its build none. They cannot be edited after the request is recorded. An
amend, which answers review notes on an open pull request, does not read
them.

The item page shows them on the **Activity** tab, in the entry for the
request, beside who asked and when. The `/build` skill that
`outerlayer init` writes reads them, so a repository whose skill is older
needs `outerlayer init --update`.

## Where to go next

* [Set up your first host](/set-up-a-host): build your first item.
* [Run the runner as a service](/run-a-host-as-a-service): run it unattended,
  move it, upgrade it or remove it.
* [What a build gets](/container-builds): what a container or microVM
  build gets and can reach.
* [Destinations and secrets](/destinations-and-secrets): private registries
  and test APIs without handing a build the secret.
* [Write your own hooks](/write-your-own-hooks): your own provision, cleanup
  and report hooks.
* [Run builds on macOS](/run-builds-on-macos) and
  [Run builds on Windows](/run-builds-on-windows).
* [Build a repository's image from its devcontainer file](/build-image-from-devcontainer).
* [Troubleshoot builds](/troubleshoot-builds).
* [Security and limits](/build-security).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.