WSL protectBinfmt: Preserve Windows Executable Interop Across systemd Startup
Understand how WSL protects its binfmt_misc interop registration when systemd manages binary formats, and test the setting safely.
When Windows executable interop stops working after systemd starts, the failure may be at the Linux binary-format registration layer rather than in PATH translation or the Windows executable itself. WSL uses binfmt_misc so the Linux kernel can dispatch a Windows PE executable to WSL’s interop handler. In WSL 2, that registration is made at the shared VM level, so a registration change from a systemd-enabled distribution can affect other distributions using the same kernel.
The per-distribution /etc/wsl.conf setting [boot] protectBinfmt exists to keep WSL’s handler from being overwritten by systemd’s binary-format manager. Microsoft documentation lists it as enabled by default, but its summary wording says it prevents WSL from generating systemd units when systemd is enabled. The current upstream implementation is more specific: when interop and protection are both enabled, WSL generates protection drop-ins for systemd-binfmt.service and binfmt-support.service. Do not interpret the documentation summary as meaning that no WSL-generated systemd drop-ins exist; inspect the installed WSL behavior/version if that distinction matters operationally.
Three layers that should not be conflated
The binfmt_misc kernel interface registers handlers for file formats. A systemd binary-format service can load administrator-defined formats from configuration files. WSL interop adds its own WSLInterop handler that points to WSL’s /init, which then contacts the interop server to create a Windows process. This handler is not just a shell alias and is not equivalent to adding Windows directories to Linux $PATH.
The configuration controls are also separate. [interop] enabled determines whether the distribution supports launching Windows processes; [interop] appendWindowsPath controls whether Windows path elements are added to $PATH. [boot] protectBinfmt addresses WSL’s generated systemd integration for the binfmt registration when systemd is enabled. Turning off protection is not the same as turning off [interop] enabled, but it removes WSL’s generated protection unit behavior and can allow a systemd binary-format manager to replace or remove the WSL handler.
The WSL 2 registration’s VM-wide scope is operationally important. A distribution may appear healthy in isolation but disrupt another running distro if a shared handler is cleared. The protectBinfmt setting is local to a distro’s /etc/wsl.conf, yet the kernel interface it helps protect can be shared by WSL 2 distributions. Test multi-distro behavior rather than assuming the config’s per-distro location makes the underlying handler per-distro.
Inspect the active state before changing it
Record WSL and distro versions from Windows:
wsl.exe --version
wsl.exe --status
wsl.exe --list --verbose
The current Microsoft reference says the [boot] section is available on Windows 11 and Windows Server 2022; systemd support is for WSL 2. Verify the installed WSL package rather than assuming an older inbox runtime has the same option. In Linux, inspect the current interop and binary-format state:
grep -nE '^\[(boot|interop)\]|^(systemd|protectBinfmt|enabled|appendWindowsPath)=' /etc/wsl.conf 2>/dev/null || true
findmnt /proc/sys/fs/binfmt_misc
if test -r /proc/sys/fs/binfmt_misc/status; then
cat /proc/sys/fs/binfmt_misc/status
fi
if test -r /proc/sys/fs/binfmt_misc/WSLInterop; then
cat /proc/sys/fs/binfmt_misc/WSLInterop
else
echo 'WSLInterop entry is not visible in this distro'
fi
The handler file’s presence is evidence of registration in the visible binfmt_misc interface, not proof that Windows process creation will succeed. The interop server and its environment must also be available. Test a harmless Windows executable from Linux when interop is supposed to be enabled, and capture its exact output and exit code.
If systemd is active, inspect its view without editing generated files:
ps -p 1 -o comm=
systemctl is-active systemd-binfmt.service 2>/dev/null || true
systemctl cat systemd-binfmt.service 2>/dev/null || true
systemctl status systemd-binfmt.service --no-pager 2>/dev/null || true
Depending on the distro, systemd-binfmt.service or binfmt-support.service may not be installed or active. Missing service output is not by itself proof that WSL protection failed. WSL-generated files can live in runtime directories and be recreated at boot; do not hand-edit or permanently mask WSL-managed drop-ins as a first response.
What the protection does in the current WSL source
The upstream WSL implementation checks both interop state and protectBinfmt before creating the systemd integration. It installs overrides for systemd’s binary-format service and the distro’s binfmt-support service. The override restores WSL’s own registration after the standard service executes; it also adjusts stop behavior so shutdown does not unregister the WSL entry. This matters because standard binary-format management can reconcile the kernel registrations against administrator-provided definitions, while WSL’s interop handler is generated by WSL rather than being an ordinary distro-owned entry.
The feature is a compatibility safeguard, not a replacement for a distro’s own binary-format configuration. If a workload relies on QEMU, Java, or another binfmt_misc handler, test those handlers too. The active table is shared at the VM level in WSL 2 according to Microsoft’s technical interop documentation, so registration collisions and cleanup can have broader effects than a single shell. Keep the authoritative definition and ownership clear for each handler.
The normal systemd service registers formats from binfmt.d; its upstream unit also defines a stop action that unregisters formats managed through that service. Since WSL 2’s WSLInterop registration is VM-wide, a service lifecycle can matter outside the distribution where it ran. Current WSL source generates an override that clears the inherited ExecStop behavior and re-registers WSLInterop after the format service runs. If you operate custom emulation or language runtimes, document their registration owner, matching magic or extension, and expected interpreter path so a later package update does not silently change which handler receives a file.
Avoid debugging this with echo -1 > /proc/sys/fs/binfmt_misc/status; that is a destructive flush operation against the visible registry. If you need to inspect a custom registration, read its entry file and the corresponding configuration source, then reproduce any change in a disposable distro. After a WSL update, compare the observable handler and actual process-launch test rather than depending on the exact runtime drop-in filenames.
The exact generated file layout is an implementation detail and can change with WSL updates. Use the published source to understand current behavior, but do not make application scripts depend on internal filenames or write generated drop-ins directly. The stable operational contract is the documented protectBinfmt option and its purpose. If inspecting source code for a particular WSL release, pin the source revision matching the installed package where available.
When would an operator disable it?
The default should normally remain enabled. A specialist may need to disable the generated integration to test a custom systemd or binfmt lifecycle, but that changes how WSL’s handler is preserved. Before doing so, identify how WSLInterop will remain registered after systemd-binfmt starts and stops, and how other running WSL 2 distributions will be affected. Do not disable the option just because a systemd-binfmt unit logs a warning; first determine whether Windows executable interop actually fails and whether the warning is benign for the distro’s current configuration.
If a distro owns a format manager, ensure its configuration does not clear entries it does not own. Inspect its documented service behavior, binfmt files, and stop hooks. Do not broadly flush /proc/sys/fs/binfmt_misc/status on a shared VM: a flush can remove registrations required by other distributions. Avoid copying a workaround from a GitHub issue without confirming WSL version, kernel, distro, and exact reproduction.
To make a controlled diagnostic change, preserve the current config, edit only the target distro’s [boot] section, stop that distro, then test both that distro and a second running WSL 2 distro. Do not modify multiple settings in the same test. If the result is worse, restore the prior setting and perform a full WSL VM restart before deciding that rollback succeeded.
Acceptance test for the interop path
For a baseline, start two WSL 2 distributions. In each, capture /proc/sys/fs/binfmt_misc/WSLInterop, systemctl status systemd-binfmt, and the WSL version. From each distro, execute a harmless Windows program such as cmd.exe /c ver if Windows executable interop is enabled. Then restart or terminate only the distro under test and repeat the checks in the other distro. Record whether the shared VM remained running, because a full wsl --shutdown changes the test condition.
After a configuration change, verify three independent outcomes: WSLInterop is registered where expected, the Linux command can reach the interop server and start a Windows process, and any additional distro-owned binfmt_misc entries remain available. A passing systemctl status alone is insufficient. A passing cmd.exe call in one distro does not prove another distro’s registrations survived.
The test is version-sensitive because WSL’s init implementation and kernel capabilities evolve. Capture Windows build, WSL package version, Linux kernel release, distro release, systemd version, /etc/wsl.conf, and exact reproduction steps. If the handler disappears only after one distro is terminated, investigate the shutdown path separately from startup protection; do not assume protectBinfmt solves every possible global cleanup behavior.
Practical guidance
Keep protectBinfmt at its documented default unless a measured requirement demands otherwise. Treat WSLInterop as a WSL-owned registration, keep distro-owned handlers declarative, and avoid destructive writes to the shared binfmt table. When interop fails, check the handler, service behavior, [interop] enabled, PATH expectations, and WSL version as separate layers. This yields a precise diagnosis without confusing systemd unit generation with the broader Windows/Linux executable bridge.
Related:
- How WSL Lets Linux and Windows Executables Call Each Other
- Why WSL Didn’t Support systemd at First, and How It Works Now
Sources: