Skip to main content
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.
  • The GitHub App installed on the repository. Otherwise every build fails at provision with repository_not_in_installation. See Connect a repository and GitHub App permissions 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. On Windows, follow 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.
  • 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.
  • 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.
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:
The dialog that shows the new key also shows this command, filled in.

Set it up

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.
  • 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.
  • 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.
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 before you use it.
runner init then installs and starts the service that keeps the runner running; see 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):
/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.
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.
To use your own isolation, see 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:
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:
An API key works too, as ANTHROPIC_API_KEY=<key>. Any secret a destination reads goes in the same file. See What the service sets.

Check the host before it takes work

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

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:
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:
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. Every key is in The config file, and every command in Runner commands.