Recently, I switched my development environment to WSL (Windows Subsystem for Linux).
My goal was to get the best of both worlds:
- Windows for its stability and compatibility with applications such as Microsoft Office and other software that isn't available natively on Linux.
- Linux for development, where its CLI, package ecosystem, networking tools, and overall development experience are often more convenient.
This setup has worked well for me, with one notable exception: PostgreSQL.
The Problem
I decided to keep my PostgreSQL server and pgAdmin installation directly on Windows rather than moving them into WSL or Docker.
For my use case, this was simpler and more straightforward. PostgreSQL runs as a normal Windows service, pgAdmin works normally, and I don't need to manage another PostgreSQL container or installation inside WSL.
However, this introduced a networking issue.
WSL's default networking mode
By default, WSL 2 uses a NAT-based virtual network. WSL runs inside a lightweight virtualized environment with its own network interface and IP address.
This means that localhost inside WSL refers to the WSL environment itself, not necessarily the Windows host.
For example, if PostgreSQL is running on Windows:
Windows
└── PostgreSQL :5432
WSL
└── Application :8000
An application running inside WSL cannot always simply connect to:
localhost:5432
because localhost from the WSL application's perspective refers to WSL.
Using the Windows host IP
One workaround is to connect to Windows through the IP address of one of its network interfaces, for example:
192.168.1.100:5432
This works, but it introduces several inconveniences.
First, I had to configure PostgreSQL to accept connections through that address instead of only allowing local connections.
Second, the address isn't necessarily stable. Depending on the network I'm connected to, Windows may have a different IP address.
This means a database configuration that looks like this:
DB_HOST=192.168.1.100may eventually need to become:
DB_HOST=192.168.1.105or perhaps use the IP of another network interface.
It also makes project configuration unnecessarily confusing.
I even encountered situations where the VMware virtual network interface introduced another possible host IP, making the setup even more confusing.
The Solution: WSL Mirrored Networking
The solution turned out to be surprisingly simple: WSL's Mirrored networking mode.
Instead of giving WSL its own isolated NAT-based network, Mirrored mode allows WSL to mirror the network interfaces available on the Windows host.
This changes the networking model significantly.
With the traditional NAT configuration:
Windows
┌─────────────┐
│ Windows │
│ │
│ PostgreSQL │
│ :5432 │
└──────┬──────┘
│
NAT
│
┌──────▼──────┐
│ WSL │
│ │
│ Application │
└─────────────┘
WSL has a separate virtualized network.
With Mirrored mode, WSL's networking is much more closely integrated with the Windows host:
Windows
┌───────────────┐
│ PostgreSQL │
│ :5432 │
└───────┬───────┘
│
Mirrored networking
│
┌───────▼───────┐
│ WSL │
│ │
│ Application │
└───────────────┘
One of the practical benefits is that services running on Windows can be reached from WSL using:
localhost
or:
127.0.0.1
This means I can keep the same database configuration I would normally use when running everything directly on Windows.
Enabling Mirrored Networking
1. Open your Windows user directory
Open File Explorer and navigate to your Windows user directory, which is usually located at:
C:\Users\<YourUsername>
2. Open .wslconfig
Look for a file named:
.wslconfig
If it doesn't exist, create it.
3. Enable Mirrored networking
Open .wslconfig with a text editor and add:
[wsl2]
networkingMode=mirrored
Save the file.
4. Restart WSL
Open PowerShell and run:
wsl --shutdown
This completely shuts down the WSL virtual machine so that the new configuration can be applied.
Then launch your WSL distribution again.
5. Connect to Windows services using localhost
Your WSL applications should now be able to access services running on Windows through localhost.
For PostgreSQL, for example:
localhost:5432
or:
127.0.0.1:5432
So your application configuration can remain simple:
DB_HOST=localhost
DB_PORT=5432
instead of having to track the Windows host's current network address.
Why This Works Better for My Development Setup
For me, this removes one of the most annoying parts of using WSL alongside Windows-hosted services.
I can keep:
- PostgreSQL on Windows
- pgAdmin on Windows
- Development tools and applications inside WSL
- Windows applications such as Microsoft Office
- Linux-based development workflows
while still using the familiar:
localhost
for local services.
It also means that I don't need to update database configurations every time my machine changes networks or Windows assigns a different IP address to one of its interfaces.
In other words, Mirrored networking makes WSL feel much less like a separate machine and more like a Linux development environment integrated into Windows.
For a development setup where some services remain on Windows while the actual development environment lives inside WSL, this is a small configuration change that makes a significant difference.