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

# Run builds on Windows

> Isolated builds on a Windows machine need Docker Engine inside WSL2, or the runner image on Docker Desktop.

On Windows the runner runs inside WSL2, and isolated builds need Docker
Engine inside WSL2 too. A runner installed in WSL2 does not use Docker
Desktop. With Docker Desktop alone, `outerlayer runner init` refuses unless
you pass `--allow-process-builds`. Builds then use `builtin:process`, record
`shared-user`, and are not isolated. See
[The process fallback](/write-your-own-hooks#the-process-fallback).

The runner image accepts Docker Desktop on Windows, with an engine of version
26 or later. It is not yet verified end to end: no one has run a build that
way and reported the result. See
[Choose how to run the runner](/run-work-on-your-machines#choose-how-to-run-the-runner).

## Set it up

1. In a WSL2 shell, install and start Docker Engine 26 or later, following
   Docker's instructions for your Linux distribution. Install Docker's
   Buildx plugin too, version 0.18 or later. The runner builds build images with
   it; see [Image builds](/run-a-host-as-a-service#image-builds).

2. Check the shell and the engine:

   ```bash theme={"system"}
   # Prints a line: this shell is WSL2.
   grep -i microsoft /proc/version
   # Does not print "Docker Desktop".
   docker info --format '{{.OperatingSystem}}'
   # Prints 26 or later.
   docker version --format '{{.Server.Version}}'
   ```

3. From the same shell, follow [Set up a host](/set-up-a-host).
   `outerlayer runner init` names `builtin:container` when it finds Docker
   Engine in WSL2. Otherwise it refuses and says what is missing;
   `--allow-process-builds` names `builtin:process` instead, with no isolation.

4. Run `outerlayer doctor`. The `Build isolation` line should say
   `builtin:container`.

## Keep the runner running

WSL2 shuts a distribution down once no Windows program is using it, so the
runner would stop when you close your terminals or log out.
`outerlayer runner init` sets up what prevents that. You write no service
file, and the command is the same as on Linux.

* It writes the systemd unit inside WSL2 and enables it.
* It makes a Windows scheduled task that starts the distribution when you log
  in and keeps it running, with `schtasks.exe` through WSL2's Windows
  interop. The task has the unit's name, `outerlayer-runner`. The runner runs
  from when you log in, not before. For a runner that runs before anyone
  logs in, use Linux.

If systemd is off in the distribution, setup turns it on in `/etc/wsl.conf`
and stops there. Run `wsl --shutdown` once from Windows, open the
distribution again, and run `outerlayer runner init` or
`outerlayer runner install --service` again. Setup makes no unit and no task
until then.

If `schtasks.exe` cannot start, setup does not check for the task. It
prints two `schtasks` commands to run in Windows: `schtasks /Query /TN
outerlayer-runner` to check for the task, and `schtasks /Create /F` to make
it. It does the same under `sudo`, which drops `WSL_DISTRO_NAME`, the variable
that names the distribution. Without that name, setup makes no task, and the
printed command holds a placeholder for the name `wsl -l` shows. Run setup
without `sudo` so it can make the task. To remove the unit, run `outerlayer runner uninstall --service`. If
`schtasks.exe` cannot start, uninstall prints `schtasks /Delete /F /TN
outerlayer-runner` to run in Windows.
See [Run the runner as a service](/run-a-host-as-a-service).

## Windows access from the runner's distribution

Setup turns off three WSL2 defaults in the runner's distribution. It writes
them to `/etc/wsl.conf`:

```ini theme={"system"}
[interop]
enabled=false
appendWindowsPath=false

[automount]
enabled=false
```

* **Interop.** With interop on, any Linux process in the distribution can
  start a Windows program. Windows runs it as the active Windows user.
* **The Windows `PATH`.** WSL adds it to the distribution's `PATH`, so a shell
  finds `cmd.exe` and `powershell.exe` by name.
* **Drive automount.** WSL mounts each Windows drive under `/mnt`. A process
  can then read and change the drive with your Windows rights.

A build that escapes its container as an account other than root would reach
your Windows account through these. The runner needs none of them after setup.

Setup keeps every other line of `/etc/wsl.conf`. It writes the file only if a
line must change. Uninstall leaves the three settings in place, because
another runner can share the distribution. To turn Windows access back on,
delete the three lines and restart the distribution.

### Restart the distribution

WSL reads `/etc/wsl.conf` only when the distribution starts. Setup does not
restart it. Until you restart, interop and the drive mounts stay on.

A restart stops every running build. Restart when no build runs. From
Windows, run these commands:

```powershell theme={"system"}
wsl --terminate <distribution>
schtasks /Run /TN outerlayer-runner
```

Use the name that `wsl -l` shows for `<distribution>`. After the restart,
`schtasks.exe` cannot start in the distribution. A later run of setup or
uninstall prints the `schtasks` command to run in Windows.

A host set up before this change gets the settings the next time you run
`outerlayer runner install --service`. Then restart the distribution.

### Before the restart

* Use a distribution that runs nothing but the runner. After the restart, you
  lose `code .`, `explorer.exe` and `/mnt/c` in it. Use another distribution
  for Windows tools.
* Keep the runner's config, its directory and its Node in the distribution's
  own file system, not under `/mnt`. The service cannot start otherwise.
* Check Git's credential helper. If it is Git Credential Manager for Windows,
  Git fails after the restart.
* Check `~/.docker/config.json`. If it holds `"credsStore": "desktop.exe"`,
  which Docker Desktop leaves behind, Docker cannot get credentials. Pulls and
  image builds fail. Remove the key.
* A copy of a distribution made with `wsl --export` and `wsl --import` starts
  as root. Set `[user] default=` in that copy's `/etc/wsl.conf`. Otherwise
  setup runs as root, and so does the service.

### What the settings do not stop

Root in the distribution can still mount a Windows drive by hand. The
runner's account is often in the `docker` group, which gives root in practice.
So the settings remove the default paths to Windows. They do not stop root.
A drive listed in `/etc/fstab` still mounts at start. WSLg still shows windows
on your desktop. A build that must never reach your account needs microVM
builds, which WSL2 does not offer.

## What doctor says

| The machine has | `outerlayer doctor` reports |
| - | - |
| Docker Engine in WSL2, hooks `builtin:container` | Isolation `container`. |
| Docker Desktop only, hooks `builtin:process` | Isolation `shared-user`, and the fix: install Docker Engine 26 or later and set `runner.hooks.provision` and `runner.hooks.cleanup` to `builtin:container`, or run the runner image on Docker Desktop. |
| Docker Desktop, hooks `builtin:container` | A failure: every build would fail at provision. The fix is Docker Engine inside WSL2, the runner image on Docker Desktop, or `builtin:process`. |

Every other line `doctor` prints is in [`outerlayer doctor`](/reference/cli-capture#outerlayer-doctor).


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