> ## 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.

# Update a runner

> The commands that move a runner to a new CLI version, for each way a runner runs, and how to check that the update worked.

Most runners update themselves. Update a runner by hand when one of these says it needs an update:

* its log;
* the **Runners** section on the factory's **Settings**, **General** page;
* an email from OuterLayer;
* a Work page item that reads "*runner* needs an update".

## Run the update

How a runner updates depends on where the runner itself runs. It does not depend on where its builds run. A runner whose builds run in containers and one whose builds run in microVMs update the same way when they run in the same place.

Find how you started the runner, then run the commands for it.

| You started the runner with | Where the runner runs | Updates itself |
| - | - | - |
| `docker run … ghcr.io/outerlayerai/runner` | [In the runner image](#the-runner-image), on Docker, OrbStack or Docker Desktop | No |
| `outerlayer runner install --service` or `outerlayer runner init` on Linux or in WSL2 | [As a service on the machine](#a-service-runner) | Yes |
| `outerlayer runner init --vm` on a Mac | [As a service in a Lima VM on the Mac](#a-runner-in-a-lima-vm-on-a-mac) | Yes |
| `outerlayer runner start` in a terminal | [In that terminal](#a-runner-started-in-a-terminal) | No |

The commands install the newest release. If the factory's **Runner updates** setting is **Hold at a version**, put that version in place of `latest`.

### The runner image

Pull the new image, then replace the container with one made from it. Keep the same volume.

First, print the mounts of the running container. The new container needs the same ones:

```bash theme={"system"}
docker inspect outerlayer-runner --format '{{range .Mounts}}{{if .Name}}{{.Name}}{{else}}{{.Source}}{{end}}:{{.Destination}}{{"\n"}}{{end}}'
```

The line that ends in `:/root/.outerlayer` is the runner's own state: its config and its host key. It is `outerlayer-runner:/root/.outerlayer` if you started the runner with these docs. If it names a folder, such as `/Users/you/outerlayer-runner:/root/.outerlayer`, use that folder in place of `outerlayer-runner` below. A new or different mount starts an empty runner, and the factory refuses its runner key.

If you already removed the old container, its state is still on disk, in the volume or folder that holds `config.json` and `runner/host-key.pem`. If none holds them, start with an empty mount. The factory then refuses the runner key, because the key is bound to the old host key. Do one of these:

* In **Settings → API keys**, select the clear host key icon, a crossed-out link, on the runner key's row. The icon shows only on a key that a host has used.
* Create a new runner key and put it in your env file. A new key is bound to no host.

The runner's next claim binds its new host key.

Then replace the container:

1. `docker pull` downloads the new image.
2. `docker stop` and `docker rm` remove the old container. It still runs the old image.
3. `docker run` starts a new container from the new image, with the mounts you printed and the same `--env-file`.

```bash theme={"system"}
docker pull ghcr.io/outerlayerai/runner:latest
docker stop outerlayer-runner
docker rm outerlayer-runner
docker run -d --name outerlayer-runner --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v outerlayer-runner:/root/.outerlayer \
  --env-file runner.env \
  ghcr.io/outerlayerai/runner:latest
```

The image holds the CLI, so a new image is a new CLI for the runner. There is nothing to install with npm for it. The `outerlayer` command on the machine itself is a separate copy: update it with `npm install -g @outerlayer/cli@latest`.

The volume holds the runner's config and its host key, so the new container is the same runner. Builds survive the change. A container never moves to a newer image by itself, even when it restarts. Docker reads the `latest` tag only when you pull.

### A service runner

A service runner updates itself unless `runner.autoUpdate` is `false`. To update it by hand:

```bash theme={"system"}
npm install -g @outerlayer/cli@latest
"$(npm prefix -g)/bin/outerlayer" runner install --service
```

Use the second command as written. The `outerlayer` launcher in `~/.local/bin` still runs the old version. Do not restart the service: it restarts on the new version once its running builds allow it. For a runner with your own hooks, leave out `--service`.

### A runner in a Lima VM on a Mac

The runner in the VM updates itself. To update it by hand, open a shell in the VM:

```bash theme={"system"}
limactl shell outerlayer-runner
```

Then run these commands in that shell:

```bash theme={"system"}
sudo npm install --global @outerlayer/cli@latest
"$(npm prefix -g)/bin/outerlayer" runner install --service
```

Installing the CLI on the Mac does not update the runner in the VM.

### A runner started in a terminal

A runner started in a terminal never updates itself.

```bash theme={"system"}
outerlayer runner stop
npm install -g @outerlayer/cli@latest
outerlayer runner start
```

On Linux, run `outerlayer runner install --service` in place of `outerlayer runner start`. The runner then runs as a service and updates itself from then on.

## Check that it worked

1. Run `outerlayer --version` where the runner runs. For the runner image, run `docker exec outerlayer-runner outerlayer --version`. It prints the new version.
2. Run `outerlayer doctor` in the same place. It reports no failure.
3. Open the factory's **Settings**, **General** page. The **Runners** section shows the runner as **Ready**, at the new version.

The runner takes work again on its next poll. Rerun each build that stopped while the runner was behind: select **Rerun on a host** on the item's page.

If `outerlayer doctor` says the microVM helper needs reinstalling, run `outerlayer runner install --service --replace`. See [Update the microVM helper](https://docs-stg.outerlayer.ai/run-a-host-as-a-service.md#update-the-microvm-helper).

## When a runner must update

The gateway refuses work from a runner that falls outside the [support window](https://docs-stg.outerlayer.ai/run-a-host-as-a-service.md#the-support-window). The window is 30 days after the gateway names a newer version. The factory's owners and admins get an email 7 days before, and another when the gateway refuses the runner.

To stop or limit updates for every runner of a factory, see [Pausing or holding updates for a factory](https://docs-stg.outerlayer.ai/run-a-host-as-a-service.md#pausing-or-holding-updates-for-a-factory).


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