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

# Set up your first host

> Install the runner on one machine, give it a key and Claude's credential, and build your first item.

This page takes one Linux machine from nothing to a finished build. On the
way, `outerlayer runner init` installs the service that keeps the runner
running.

## Before you begin

You need:

* **Node.js 22 or later**, and the CLI:
  `npm install -g @outerlayer/cli`. A service needs a fixed path to start,
  so use the global install rather than `npx`.
* **A runner key.** In the factory, open **Settings → API keys**, choose
  **Create API key**, and pick the **Runner** preset. It holds what a runner
  needs, including `work.claim` and `hosts.ingest`. Name the key after the
  machine. The key is shown once. Give each host its own key: a runner key
  works only from the first host that uses it. See [API keys](/api-keys).
* **The GitHub App installed on the repository.** Otherwise every build
  fails at provision with `repository_not_in_installation`. See
  [Connect a repository](/connect-repository) and
  [GitHub App permissions and build tokens](/github-app-and-build-tokens).
* **Docker Engine 26 or later, on Linux or WSL2,** usable by the runner's
  account. Docker Desktop alone is not enough. On a Mac, follow
  [Run builds on macOS](/run-builds-on-macos). On Windows, follow
  [Run builds on Windows](/run-builds-on-windows).
* **On a Mac, Lima.** `outerlayer runner init --vm` uses it to run the
  Linux VM that the runner lives in. Install it with `brew install lima`; see
  [Lima's install page](https://lima-vm.io/docs/installation/).
* **Docker's Buildx plugin, 0.18 or later**, and access to Docker Hub. The
  runner builds each repository's image in a builder of its own. It is
  `docker-buildx-plugin` in Docker's apt repository. Without it the runner
  claims nothing. See [Image builds](/run-a-host-as-a-service#image-builds).
* **For microVM builds:** x86\_64 Linux where the runner's account can open
  `/dev/kvm`. Without it, builds run in containers.

## Give the runner its key

A factory key outranks an account login. If you also use `outerlayer` on
this machine, such as on your own Mac, do not log in with the runner key:
it would replace your own login in `~/.outerlayer/config.json`, and your
sessions would switch factory. Give the runner a config file of its own
instead. Every runner command takes `--config <path>` to use it.

```bash theme={"system"}
mkdir -p ~/.outerlayer-runner
( umask 077; printf '{"url":"https://api.outerlayer.ai","apiKey":"%s","appId":"<factory-id>"}\n' "$RUNNER_KEY" > ~/.outerlayer-runner/config.json )
```

The **Factory Id** is on **Settings → General**. Keep the file in a
directory of its own: the runner keeps its host key, its install layout and
`runner.env` beside it. In the steps below, add
`--config ~/.outerlayer-runner/config.json` to each `outerlayer runner`
command, and keep `runner.env` in `~/.outerlayer-runner/`.

On a machine that only runs the runner, you can log in instead. The key is
read from stdin, so it stays out of your shell history:

```bash theme={"system"}
echo "$RUNNER_KEY" | outerlayer login --url https://api.outerlayer.ai --app-id <factory-id>
```

The dialog that shows the new key also shows this command, filled in.

## Set it up

```bash theme={"system"}
outerlayer runner init
```

`runner init` writes the runner's block of the config file: `~/.outerlayer/config.json`,
or the file you gave with `--config`. It
names the built-in hooks that fit the machine: `builtin:vm` on x86\_64 Linux
whose account can open `/dev/kvm`, else `builtin:container` where Docker
Engine 26 or later runs. The full rule is in
[`outerlayer runner init`](/reference/cli-runner#outerlayer-runner-init).

* On macOS it offers to run the runner in a Linux VM. `--vm` says yes and
  `--no-vm` says no; see [Run builds on macOS](/run-builds-on-macos).
* On any other machine that cannot isolate a build, it refuses and writes
  nothing. Run it again with `--allow-process-builds` to name
  `builtin:process`, where builds run as your account with no isolation.

<Warning>
  `builtin:process` has no isolation. A build runs as your account, can read
  your files and the runner's own environment, and can reach the network
  directly. See [Security and limits](/build-security) before you use it.
</Warning>

`runner init` then installs and starts the service that keeps the runner
running; see [Keep it running](#keep-it-running).

The service claims nothing yet. It waits for the two steps below: a command
for each queue, and Claude's credential.

### Edit the commands

`runner init` writes a placeholder command for each queue. The runner claims
nothing while a queue's command is still the placeholder. Edit
`runner.commands` to start your agent. The runner block then looks like this
(abridged):

```json theme={"system"}
{
  "url": "https://api.outerlayer.ai",
  "apiKey": "<key>",
  "appId": "<factory-id>",
  "runner": {
    "queues": ["implement", "amend"],
    "concurrency": 1,
    "commands": {
      "implement": "claude -p \"/build\" --permission-mode bypassPermissions",
      "amend": "claude -p \"/amend\" --permission-mode bypassPermissions"
    },
    "hooks": {
      "provision": "builtin:vm",
      "cleanup": "builtin:vm"
    }
  }
}
```

`/build` and `/amend` are skills that `outerlayer init` installs in the
repository. The running service reads the edited file on its next poll,
with no restart.

<Warning>
  By default a build can reach every public host, and can send anything it
  can read to any of them. List the hosts your builds need in
  `build.allowHosts`; see
  [Limiting the hosts a build reaches](/container-builds#limiting-the-hosts-a-build-reaches).
</Warning>

To use your own isolation, see [Write your own hooks](/write-your-own-hooks).

### Give the runner Claude's credential

The runner needs a Claude credential in its environment. A build never sees
the credential itself. On a host with no browser, create one:

```bash theme={"system"}
claude setup-token
```

The service reads its environment from `runner.env`, beside the config
file. Make the file readable only by the runner's account, add the token,
and restart the service, which reads the file only when it starts. The paths
below are for a config in `~/.outerlayer-runner/`; with the default config,
use `~/.outerlayer/runner.env`:

```bash theme={"system"}
touch ~/.outerlayer-runner/runner.env && chmod 600 ~/.outerlayer-runner/runner.env
echo "CLAUDE_CODE_OAUTH_TOKEN=<the token>" >> ~/.outerlayer-runner/runner.env
sudo systemctl restart outerlayer-runner
```

An API key works too, as `ANTHROPIC_API_KEY=<key>`. Any secret a
[destination](/destinations-and-secrets) reads goes in the same file. See
[What the service sets](/run-a-host-as-a-service#what-the-service-sets).

### Check the host before it takes work

```bash theme={"system"}
outerlayer runner check
```

`check` validates the config, confirms the key works, and prints the
settings the runner would use. It takes the Claude credential from your
shell, and from the `runner.env` beside the config when the shell has none.
It names the file when it used it. It exits 1 and says what to fix when:

* a queue's command is still the placeholder.
* a `claude` command has a `--permission-mode` Claude Code does not accept,
  such as `bypassPermissons`. The message names the command and the value.
* built-in hooks find the credential neither in your shell nor in
  `runner.env`.
* the key does not hold `work.claim`. Create it with the Runner preset.

If it prints `credentials unverified`, the gateway did not answer
normally. Run it again later.

`outerlayer doctor` reports what isolation a build gets on this machine. It
reads the credential from the service's `runner.env`.

## Keep it running

`outerlayer runner init` already set up what keeps the runner running, with
the same command on every system. You write no service file. It restarts
the runner after a crash, a reboot and a self-update. It asks for `sudo`
where it needs root, and names each command first.

* **Linux:** the runner runs from boot, as a systemd service.
* **macOS:** the runner runs from login, as the same service inside the
  Linux VM that `--vm` makes.
* **Windows:** the runner runs from login, as the same service inside
  WSL2.

To set it up again after the config changes, or to remove it, see
[Run a host as a service](/run-a-host-as-a-service).

### Run the runner in a terminal instead

The service and a terminal runner cannot run on the same config. To run the
runner in a terminal, remove the service first:

```bash theme={"system"}
outerlayer runner uninstall --service
outerlayer runner start
```

The runner then needs the credential in that shell's environment. It
refuses to start, naming the problem, when the config is wrong, a hook is
not an executable file, or the key is refused. Its first claim binds the
runner key to this machine; see
[The host key](/run-a-host-as-a-service#the-host-key).

## Build your first item

Ask for a build as yourself, not with the runner key. A build requested
with a runner key gets no repository tokens, so it fails. Either:

* choose **Build** beside the issue on the Work page, or
* run this from your own machine:

```bash theme={"system"}
outerlayer work build --issue <n>
```

The first build of a repository also builds its image, so it takes longer.
The first microVM build also downloads Firecracker and a guest kernel from
github.com.

## Watch it run

From a second terminal on the host:

```bash theme={"system"}
outerlayer runner status
outerlayer runner logs <item> -f
```

`status` lists running builds and their stage. `logs -f` follows one
item's build log. `outerlayer runner status --recent` shows finished
builds. The session and the pull request land on the item's page.

If something goes wrong, see [Troubleshoot builds](/troubleshoot-builds).
Every key is in [The config file](/reference/config-file), and every
command in [Runner commands](/reference/cli-runner).


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