docker run
on any Linux machine with Docker Engine, or on a Mac with Docker Desktop. You install no CLI and you write no
config file. Docker’s restart policy keeps it running, and its builds survive
the container restarting.
The service runner in Run the runner as a service
stays the choice for a runner that builds in microVMs.
Before you begin
You need:- A Linux machine with Docker Engine 26 or later, or a Mac with Docker Desktop. The engine behind Docker Desktop must also be version 26 or later. The image refuses to start with an older engine and names its version. See Docker Desktop.
- A runner key. It is an API key with the Runner preset. Create it as described in Set up your first host.
- The factory’s id. It is the Factory Id on Settings → General.
- A Claude credential. Create a token with
claude setup-token, or use an Anthropic API key.
/dev/kvm and a cgroup for each build on the host.
Docker Desktop
On a Mac, the runner in the image runs on Docker Desktop’s Linux VM. The runner sees that VM, not the Mac. Builds run as containers in the VM and recordcontainer.
A build’s container cannot reach the Mac. The image build runs outside the
runner’s tunnel, so it can reach the Mac’s local services. See
Security and limits.
The VM must be large enough for the runner’s default build limits. Each build
is limited to 8g of memory, 4 CPUs and 40g of disk. Docker Desktop’s
default VM can be smaller.
- Disk. The runner claims work only when the VM’s disk has
runner.housekeeping.minFreeDiskfree. The default is 40 GB. The VM’s disk also holds every image and volume on the Mac. - Memory. The runner claims work only when the VM’s memory is at least
runner.concurrencytimesrunner.build.memory, plus 2 GiB. With the defaults, that is about 10 GiB. - CPUs. A build never gets more CPUs than the runner can use. A VM with fewer than 4 CPUs gives each build all of its CPUs.
runner.build.memory, runner.build.cpus, runner.build.disk and
runner.housekeeping.minFreeDisk in the config file in the volume. Edit it
with docker exec, as What the volume holds
describes.
Docker Desktop stops every container when it restarts, builds included. The
runner then ends each build with the exit code it recorded. A build with no
recorded exit code ends as interrupted, so the item can be built again.
After a reboot, two settings bring the runner back with no one starting it:
- In Docker Desktop’s settings, turn on Start Docker Desktop when you sign in.
- Start the runner container with
--restart unless-stopped, as the command below does.
Start the runner
Put the settings in a file, so they stay out of your shell history. The file holds the runner key, so give it to your account only.runner.env
<version> with a CLI version, such as 0.22.2. There is no latest tag.
A run command names its version, so a runner never changes under a running
factory.
The file must set these variables:
OUTERLAYER_AGENT is optional. It names the agent the builds run, and it
defaults to claude.
What the volume holds
The volumeouterlayer-runner holds everything the runner keeps:
- the config file, written from the variables on the first start;
- the host key, which binds the runner key to this host;
- the state of running builds, and the mirrors of repositories;
- the metadata of the runner’s image builder.
OUTERLAYER_URL, OUTERLAYER_APP_ID, OUTERLAYER_API_KEY or
OUTERLAYER_RUNNER_NAME disagrees with that config. The message names the factory by
URL and app id, the key by its last four characters, and the runner by name. It
never prints a key in full. To move a runner to another factory, key or
name, use a new volume.
Other settings, such as queues, concurrency and the time limit, live in the
config file in the volume. Edit it with docker exec. The runner reads the
file again when it changes. See the config file reference.
Check that it runs
OUTERLAYER_RUNNER_NAME.
Pin the image by digest
A tag can move. A digest cannot. To pin the image, find the digest of the version you tested:docker run:
gh attestation verify oci://ghcr.io/outerlayerai/runner@sha256:<digest> --owner outerlayerai.
Drain before maintenance
docker stop leaves the builds running and exits the runner at once. To wait
for the builds instead, drain the runner first:
outerlayer runner status in
the container to see what is left:
docker stop outerlayer-runner when no build is left. Do not expect the
container to exit by itself: with --restart unless-stopped, an exit would
start a runner that claims work again.
Stop the runner
docker stop outerlayer-runner takes no more work, leaves every build running
and exits with status 0, before Docker’s 10-second kill. The container that
starts next, with the same volume, picks the builds up.
A build keeps its lease only while a runner renews it. A recreate that takes
longer than the lease ends the build. A build’s open connections fail while no
runner runs. A new connection waits up to 30 seconds for a runner, and then
fails.
Update the runner
A runner in the image does not update itself. The image tag names the version that runs, so an update would hide in the volume, wheredocker images cannot
show it. An update is a new image.
The runner tells you when the factory wants another version. It logs the tag
once. outerlayer runner status and outerlayer doctor name it too. If the
factory refuses the runner as too old, the runner claims nothing until you
update it.
To update: