outerlayer runner init names built-in
hooks: builtin:vm or builtin:container where the
machine can isolate a build, and builtin:process
where it cannot and you allow it. The full rule is in
outerlayer runner init.
Write your own hooks to give builds an isolation of your own, such as a
cloud VM or a sandbox your platform team runs. The runner runs the
provision hook before the command, the cleanup hook after it, and an
optional report hook at both ends. See
What happens to one build.
Name your scripts in the runner block of ~/.outerlayer/config.json:
provision and cleanup are required. report is optional and is never a
builtin: value. See the config file.
The build’s variables
Every hook and the command receive these, and so does everything they start. They also inherit the runner’s own environment, except anyOUTERLAYER_* variable and the secrets a destination’s header names. A
container build gets only the variables named in runner.build.variables.
For anything else about the item, call the CLI:
Hooks are executables, commands are shell lines
A hook path is run directly: no shell, no arguments, no variables expanded."/opt/outerlayer/provision.sh" works. "$HOME/bin/provision.sh" and
"/opt/outerlayer/provision.sh --fast" fail with an error naming the path.
Put arguments and expansion inside the script. Command lines run through
/bin/sh -c, so variables in them expand.
A hook that goes quiet is ended. A hook silent in its log for
idleLimitMinutes gets SIGTERM, then SIGKILL, logged as
#9 provision idle for 30m, ended. No hook may outlast timeLimitMinutes.
A provision hook ended this way fails the build. A cleanup or report hook
ended this way leaves the build’s outcome as it was. Print progress.
The provision hook
Runs before the command, in$OUTERLAYER_JOB_DIR. It receives
the build’s variables and these:
provision.out
The hook writeskey=value lines to $OUTERLAYER_JOB_DIR/provision.out:
A key the runner cannot honour, such as a relative path, fails the build at
provision and names the key.
The tunnel and the proxy
The hook can mount$OUTERLAYER_BUILD_DIR into the isolation. With an
exec, HTTPS_PROXY and its siblings point at the broker address, or at
127.0.0.1:3128 when there is none. Make that port reach the tunnel by
running the relay inside the build’s network:
--forward <port>=<socket> relays one port to one destination; see
Destinations.
Exit status
- Exit 0 runs the command. A
workdirthat does not exist fails the job with outcomefailed. - Exit 75 (
EX_TEMPFAIL) declines the item: this host cannot take it now, for example with too little disk free. The runner releases the claim with no outcome, and the item stays queued for another host or a later poll. - Any other non-zero exit fails the job with outcome
failed.
workdir. It must not wait for input.
The cleanup hook
Runs after the command, however it ends, and again on recovery for a job the runner lost. It receives the build’s variables plus:OUTERLAYER_OUTCOME:ok,failed,timed_out,idle,lease_lost,interruptedorstopped. Empty when provision exited 75. Troubleshoot builds explains each. It isokfor any command that exited 0: the runner reads the item’s pull request and checks after cleanup, and only then can release the buildincomplete.OUTERLAYER_LOG: the path of the command’s output log.OUTERLAYER_BUILD_DIR: so the hook can unmount what it mounted there.
$OUTERLAYER_JOB_DIR/reason. The runner sends it with the release, cut to
200 characters.
The report hook (optional)
Runs when a job starts, withOUTERLAYER_PHASE=start, and when it ends,
with OUTERLAYER_PHASE=end, OUTERLAYER_OUTCOME and OUTERLAYER_LOG. Use
it to comment on the issue. The log is always
$OUTERLAYER_JOB_DIR/command.log, though the start phase is not given it.
The end phase’s OUTERLAYER_OUTCOME is the
outcome the release carries. So a command that exited 0 while the item has
no pull request, or its checks still fail, is incomplete here. See
When the command exits 0.
The process fallback
A machine that cannot isolate builds can still run them through the tunnel with both hooks set tobuiltin:process. runner init --allow-process-builds sets this on a machine without Docker Engine 26 or
later; without the flag, runner init refuses there.
shared-user.
This is not a boundary. The build can read the runner user’s files, the
runner’s environment (including destination
secrets and Claude’s credential), the item key and the config file. It has
the host’s network. npm, git, curl and Node 22.21 or later honour the
proxy variables; a tool that ignores them bypasses the tunnel and shows
nothing in the release. Use builtin:container where you can.