Franken-RBE: Windows Chromium with Linux RBE via WSL2

โšก Chromium ๐Ÿ”ง C++ / Build Systems ๐Ÿ‘ค Helmut Januschka

It's alive! Stitching together WSL2, Linux RBE, and Windows SDK to build Chrome faster than nature intended.

Status: ๐Ÿ› ๏ธ Guide


External RBE for Windows is not a thing. But don't panic -- as long as you have a towel, you can make your way.

โฑ๏ธ Setup Time: 1-3 hours

Depending on your machine and internet speed. I did this on a cheap $300 mini PC (16GB RAM, Ryzen 5, 2TB SSD).

Or just give the pi.dev agent the link to this blog post and let it do the work for you.

Chromium on Windows is a monster build. If you have Linux RBE access but no Windows RBE, you can still build Windows Chrome fast: use WSL2 as the Linux host and cross-compile target_os="win".

The end result:

Official references:

Replace <user>, <host>, <wsl-ip>, and paths as needed.


๐Ÿ“‘ Table of Contents

  1. Start with a normal Windows Chromium setup
  2. Install WSL2 and give it enough memory
  3. Create the Linux checkout on WSL ext4
  4. Package the Windows SDK/toolchain for Linux
  5. The Windows SDK mount: ciopfs
  6. Generate out/Win-Cross
  7. RBE auth for external contributors
  8. SSH into WSL2 for FIDO ReAuth
  9. Install and run the Windows build

โ†’ The Workflow (TL;DR)


1. Start with a normal Windows Chromium setup

Do this first. Follow the official Windows build instructions and get a regular Windows checkout/build working before adding WSL2:

C:\src\chromium\src
C:\src\chromium\src\out\Default

You need:

Why? Because the cross-build reuses your real locally-installed Visual Studio + Windows SDK. Later we package that toolchain and feed it to Linux.

Keep this checkout separate. The WSL cross-build gets its own checkout and does not touch out\Default.


2. Install WSL2 and give it enough memory

Install WSL2 from elevated PowerShell:

wsl --install --no-distribution

Reboot, then install Ubuntu:

wsl --install -d Ubuntu-24.04 --no-launch

Chromium will OOM with WSL defaults on smaller machines. I run this on a $300 mini PC because RAM is expensive these dAIs. Create C:\Users\<user>\.wslconfig:

[wsl2]
memory=9GB
swap=48GB
swapFile=C:\\Users\\<user>\\wsl-swap.vhdx
processors=6

[experimental]
autoMemoryReclaim=gradual
sparseVhd=true

Apply it:

wsl --shutdown

The big swap file is intentional. SSD is cheaper than RAM, and it is much better for a compile to slow down than for the OOM killer to murder clang halfway through the build.


3. Create the Linux checkout on WSL ext4

Do not build under /mnt/c. It works, but it is painfully slow. Put the checkout in WSL's Linux filesystem:

sudo apt update
sudo apt install -y git python3 python-is-python3 curl lsb-release \
  build-essential file ca-certificates

cd ~
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
echo 'export PATH="$HOME/depot_tools:$PATH"' >> ~/.bashrc
echo 'export PATH="$HOME/depot_tools:$PATH"' >> ~/.zshrc
source ~/.bashrc

mkdir -p ~/chromium
cd ~/chromium
gclient config --name=src https://chromium.googlesource.com/chromium/src.git

Edit ~/chromium/.gclient:

solutions = [
  { "name"        : "src",
    "url"         : "https://chromium.googlesource.com/chromium/src.git",
    "deps_file"   : "DEPS",
    "managed"     : True,
    "custom_deps" : {},
    "custom_vars" : {
      "rbe_instance": "projects/rbe-chromium-untrusted/instances/default_instance",
    },
  },
]
target_os = ["win"]

Two important lines:

Now sync:

cd ~/chromium
gclient sync --nohooks --no-history

Then install Linux build deps:

cd ~/chromium/src
./build/install-build-deps.sh --no-prompt --no-chromeos-fonts --no-arm --no-syms

4. Package the Windows SDK/toolchain for Linux

On Windows, package the installed Visual Studio + Windows SDK:

# Git Bash on Windows
mkdir -p /c/src/chromium/win_toolchain_pkg
cd /c/src/chromium/win_toolchain_pkg

python /c/src/chromium/src/third_party/depot_tools/win_toolchain/package_from_installed.py \
  2022 -w 10.0.26100.0 --noarm --allow_multiple_vs_installs

This creates a zip named like <zip_hash>.zip.

In WSL, tell depot_tools where to find it. Add these to ~/.bashrc, ~/.bash_profile, and ~/.zshrc:

export PATH="$HOME/depot_tools:$PATH"
export DEPOT_TOOLS_WIN_TOOLCHAIN_BASE_URL=/mnt/c/src/chromium/win_toolchain_pkg
export GYP_MSVS_HASH_<wanted_hash>=<zip_hash>
export GCLIENT_SUPPRESS_GIT_VERSION_WARNING=1

<wanted_hash> is printed by Chromium's vs_toolchain.py when it asks for a specific toolchain. <zip_hash> is the zip filename you just generated.

Run hooks:

cd ~/chromium
gclient runhooks

5. The Windows SDK mount: ciopfs

The Windows SDK contains filenames that conflict on a case-sensitive Linux filesystem. Chromium solves that with a ciopfs FUSE mount:

real data:    ~/chromium/src/third_party/depot_tools/win_toolchain/vs_files.ciopfs
mount point:  ~/chromium/src/third_party/depot_tools/win_toolchain/vs_files

If this mount disappears, GN fails with something like:

FileNotFoundError: .../Windows Kits/10/bin/SetEnv.x86.json

Manual remount:

~/chromium/src/build/ciopfs -o use_ino \
  ~/chromium/src/third_party/depot_tools/win_toolchain/vs_files.ciopfs \
  ~/chromium/src/third_party/depot_tools/win_toolchain/vs_files

Make it automatic. Create /usr/local/sbin/mount-winsdk.sh:

#!/bin/bash
M=/home/<user>/chromium/src/third_party/depot_tools/win_toolchain/vs_files
B=/home/<user>/chromium/src/third_party/depot_tools/win_toolchain/vs_files.ciopfs
C=/home/<user>/chromium/src/build/ciopfs

if [ -d "$B" ] && ! mountpoint -q "$M"; then
  mkdir -p "$M"
  chown <user>:<user> "$M"
  su <user> -c "$C -o use_ino \"$B\" \"$M\""
fi

Enable it in /etc/wsl.conf:

[user]
default=<user>

[boot]
command=/usr/local/sbin/mount-winsdk.sh

Verify as the normal WSL user, not root:

mountpoint ~/chromium/src/third_party/depot_tools/win_toolchain/vs_files

If root sees Permission denied, that usually means the user-owned FUSE mount is actually active.


6. Generate `out/Win-Cross`

This is the working args.gn shape:

cd ~/chromium/src

gn gen out/Win-Cross --args='
target_os="win"
target_cpu="x64"
is_debug=false
is_component_build=false
symbol_level=1
use_lld=true
is_clang=true

use_siso=true
use_remoteexec=true
'

With those two last lines, Chromium uses SISO with the generated RBE config. The generated config points at:

projects/rbe-chromium-untrusted/instances/default_instance

A build starts with a line like:

use RBE instance "projects/rbe-chromium-untrusted/instances/default_instance"

Build the installer:

autoninja -C out/Win-Cross -j 6 mini_installer

Use a modest -j; WSL still has limited RAM even with swap. But this setup is mainly for Linux RBE-backed cross-builds. If you want a fully local Windows build, do that in the normal native Windows checkout (C:\src\chromium\src), not in WSL2.


7. RBE auth for external contributors

For external RBE access, Chromium uses Google Cloud application-default credentials for SISO/reclient:

gcloud auth application-default login

That is only the RBE/build credential. Gerrit upload/review actions also need ReAuth with a physical security key.

Note: ReAuth is only needed when you interact with Gerrit - e.g. git cl upload or git cl patch XXXX --force to pull down a CL. You could also just download and apply diffs manually from the Gerrit web UI if you want to skip this section entirely.

Inside WSL2 the key often is not visible, especially over RDP. The practical solution is: expose SSH into WSL2, then SSH in from a machine/session that can use the FIDO key.


8. SSH into WSL2 for FIDO ReAuth

โš ๏ธ Security Warning

Exposing SSH is dangerous. Read this section carefully. Consider using key-only authentication and turning off SSH when not in use.

Install and start SSH in WSL:

sudo apt install -y openssh-server
sudo tee /etc/ssh/sshd_config.d/99-wsl.conf >/dev/null <<'EOF'
Port 2222
PasswordAuthentication yes
PubkeyAuthentication yes
AllowTcpForwarding yes
GatewayPorts yes
EOF
sudo ssh-keygen -A
sudo service ssh restart

Add your public key:

mkdir -p ~/.ssh && chmod 700 ~/.ssh
curl -fsSL https://github.com/<github-user>.keys >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Expose WSL2 through the Windows host. Elevated PowerShell:

$ip = (wsl -d Ubuntu-24.04 hostname -I).Trim().Split()[0]
netsh interface portproxy delete v4tov4 listenport=2222 listenaddress=0.0.0.0
netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=2222 connectaddress=$ip
New-NetFirewallRule -DisplayName WSL-SSH-2222 -Direction Inbound -Action Allow -Protocol TCP -LocalPort 2222

From a machine with your FIDO key (e.g. your Mac), connect using luci-auth-ssh-helper:

luci-auth-ssh-helper -ssh -port=2222 <user>@<windows-host-or-ip>

Once inside WSL, ReAuth just works and RBE builds stop hanging on authentication.


9. Install and run the Windows build

Do not run chrome.exe directly from \\wsl$. Windows side-by-side assembly activation does not like the WSL network filesystem. Build and run through mini_installer.exe instead.

I keep the Windows helper scripts in a small repo so they are not copy-pasted into every write-up:

github.com/hjanuschka/wsl-rbe

After building:

autoninja -C out/Win-Cross -j 6 mini_installer

Clone or download the helpers on Windows and run:

cd wsl-rbe\scripts
set WSL_USER=<your-wsl-user>
install-and-run-winx-chrome.bat --enable-features=WebBluetooth,NewBLEGattSessionHandling

The important script is install-and-run-winx-chrome.bat. It:

  1. Closes any already-running instance of the installed Chromium build
  2. Copies mini_installer.exe from the WSL output directory
  3. Installs Chromium into %LOCALAPPDATA%\Chromium\Application
  4. Launches the installed chrome.exe in the foreground with console logging
  5. Forwards any flags you pass

There is also launch-winx-chrome.bat for re-running the already-installed build with different flags, without reinstalling.

Useful environment overrides:

set WSL_DISTRO=Ubuntu-24.04
set WSL_USER=<your-wsl-user>
set WSL_CHROMIUM_SRC=chromium\src
set WINX_PROFILE=C:\tmp\winxprofile

The Workflow

# WSL2
git credential-luci reauth        # if needed
autoninja -C out/Win-Cross -j 6 mini_installer
:: Windows
install-and-run-winx-chrome.bat --your-flags-here

That is the loop: Linux RBE does the build, WSL2 emits a Windows installer, Windows installs and runs it.

๐Ÿง builds it. ๐ŸชŸ runs it.