How We Made mirrord Run Natively on Windows
Back to blogHow We Made mirrord Run Natively on Windows<br>Jake Page<br>July 7, 2026<br>8 min read<br>For a long time, mirrord on Windows meant one thing: running it inside the Windows Subsystem for Linux. mirrord was built on Unix and already ran on Linux, so WSL was the quickest way to give Windows users a working setup without first solving native support. It worked, and it was our Windows story for a while.<br>Relying on WSL as a prerequisite has real drawbacks, though. It adds a second environment to install and keep in sync, since your tooling and configuration often have to exist both on the Windows host and inside the Linux distribution, it can be unstable, and many tools only integrate with it when they’ve been explicitly built to, with those integrations rarely at full parity with running natively. For a tool that prides itself on how easy it is to use, requiring that extra layer was never where we wanted to settle.<br>So we set out to make mirrord run natively on Windows. Most of the difficulty comes from a single difference between the platforms, so that’s where it’s worth starting.<br>New to mirrord? It runs your local process inside a live Kubernetes cluster. It works the same way for developers and for AI coding agents (Claude Code, Cursor, Codex, Copilot, Windsurf): your code runs on your machine, but mirrord routes its traffic, files, and environment variables through a target pod in the cluster.
How the mirrord-layer loads on Unix #<br>mirrord works by loading a component called the mirrord-layer into your process before any of your own code runs. It intercepts the calls your program makes, for networking, files, and environment variables, and reroutes them to your Kubernetes cluster, so the local process behaves as though it were running in the cluster.<br>On Unix, loading the mirrord-layer is straightforward because we control the environment a process starts in. Linux provides LD_PRELOAD, and macOS provides DYLD_INSERT_LIBRARIES: when that variable is set, the dynamic loader maps our shared library into the process and runs its constructor before main without our users having to change a single line of code. Controlling that environment is the whole trick: set one variable, the mirrord-layer loads before the program runs its first instruction, and that’s the entire mechanism.<br>mirrord-layer injection: Unix vs Windows<br>Everything the mirrord-layer does afterward depends on getting in that cleanly. Windows has no LD_PRELOAD and no equivalent single mechanism, so on Windows we had to implement that first step ourselves, and then reimplement it for each way Windows creates and manages processes differently from Unix.<br>Loading the mirrord-layer on Windows #<br>When we launch the target process ourselves, we inject the mirrord-layer by hand. We start the process suspended so its main thread is frozen before it runs any instructions, inject the mirrord-layer DLL on a separate thread inside the frozen process, and wait for it to signal, through a Windows named event, that it’s finished installing its hooks. Only then do we resume the main thread. The result is the same as what LD_PRELOAD provides on Linux, reached through several manual steps instead of one.<br>Some processes we don’t launch ourselves. A debugger, for example, creates the process before handing it over, so there’s no command line to wrap. For those, we inject the mirrord-layer into the process after it’s already running.<br>Child processes required more work. On Linux, a forked child inherits a copy of the parent’s memory, including the mirrord-layer, so it’s covered automatically. Windows creates processes differently, so each child a target spawns has to be injected on its own. Rather than intercepting every process-creation API separately, we hook CreateProcessInternalW, the internal function they all call through, which lets a single hook cover CreateProcessW, CreateProcessAsUserW, and the others.<br>Injecting the mirrord-layer into spawned child processes<br>Getting the IDE plugins to work #<br>Plenty of people use mirrord from the command line, but for a lot of developers, the natural way to reach for it is inside their IDE, running or debugging as they normally would. Native Windows support had to reach the IDE plugins too, and that was the harder part, the one that shows most directly what the missing LD_PRELOAD costs. The plugins drive mirrord through two internal entrypoints, pitm (Process In The Middle) and attach, neither of which you invoke yourself. The CLI uses a third, exec.<br>Getting the layer in: attach vs pitm<br>VS Code #<br>VS Code is the simpler case. It can pause a process before user code runs and give us its process ID, which is close enough to the clean injection path that the extension has run natively on Windows since version 3.69.0.<br>JetBrains #<br>JetBrains is more involved. It builds its own run command and doesn’t expose a hook point early...