Skip to content

Instantly share code, notes, and snippets.

@mbrownnycnyc
Last active August 27, 2026 22:24
Show Gist options
  • Select an option

  • Save mbrownnycnyc/145dacb59a0cb73ebebd8bc1ff5d5033 to your computer and use it in GitHub Desktop.

Select an option

Save mbrownnycnyc/145dacb59a0cb73ebebd8bc1ff5d5033 to your computer and use it in GitHub Desktop.

@DanielB's answer is an excellent explanation of why all WSL2 distributions (I like to call them "instances") share the same address. Each WSL2 instance has a separate:

Each WSL2 instance has a separate:

  • PID namespace
  • Mount namespace
  • IPC namespace
  • UTS namespace
  • WSLg System Distribution (Windows 11 only)

However, they all share the following with the parent WSL2 VM (and thus each other):

  • User namespace
  • (the only one we really care about here) Network namespace
  • Cgroup namespace
  • Device tree (other than /dev/pts)
  • CPU/Kernel/Memory/Swap (obviously)
  • /init binary (but not process)

The interesting thing is that we can use the same technology, namespaces, to "pretend" to create a separate IP address for each instance.

For the "n-tier testing" scenario you mention in the comments, this will hopefully be sufficient. We can script the creation of the namespace for each instance and (optionally) automatically enter it via the wsl command-line.

While I had a hunch that this would work, I had never played around with network namespaces before trying this out. Don't worry - It was a worthwhile learning exercise for me. Huge credit goes to this blog post for getting me 90% there.

With that said, here's a first (or second, or third ...) pass at a script to configure a network namespace that will allow each namespace in each instance to have a different IP.

Create this as something like /usr/local/sbin/wsl-netns.sh:

#!/usr/bin/env bash
instance_num=$1
#if [ -e /run/netns/]

# Create the bridge that will be common to all instances.
# Only a `wsl --shutdown` will terminate the bridge, unless
# otherwise manually removed.
if [ ! -e /sys/devices/virtual/net/br1 ]
then
    ip link add name br1 type bridge
    ip addr add 10.0.0.253/24 brd + dev br1
    ip link set br1 up
fi

# Add namespace for this instance
if [ ! -e /run/netns/vnet${instance_num} ]
then
    ip netns add vnet${instance_num}
fi

# Adds a veth pair.  The vethX
# side will reside # inside the namespace 
# and be the primary NIC inside that namespace.
# The br-vethX  end will reside in the primary
# namespace.
ip link add veth${instance_num} type veth peer name br-veth${instance_num}
ip link set veth${instance_num} netns vnet${instance_num}
# Give it a unique IP based on the instance number
ip netns exec vnet${instance_num} \
    ip addr add 10.0.0.${instance_num}/24 dev veth${instance_num}
ip link set br-veth${instance_num} up
# Add the bridged end of the veth pair
# to br1
ip link set br-veth${instance_num} master br1
ip netns exec vnet${instance_num} \
    ip link set veth${instance_num} up

# Set the default route in the namespace
ip netns exec vnet${instance_num} \
    ip route add default via 10.0.0.253
# Enable loopback fort he namespace
ip netns exec vnet${instance_num} \
    ip link set up dev lo
# Set up NAT for return traffic
iptables \
    -t nat \
    -A POSTROUTING \
    -s 10.0.0.0/24 \
    -j MASQUERADE
# Enable forwarding
sysctl -w net.ipv4.ip_forward=1

# Optional - Start a namespace for the 
# default WSL user (UID 1000).
# You can exit this namespace normally
# via the `exit` comamnd or Ctrl+D.
default_username=$(getent passwd 1000 | cut -d: -f1)
nsenter -n/var/run/netns/vnet${instance_num} su - $default_username

Not much error handling in there at the moment, but it's fairly straightforward. Set it executable and run it from an instance with:

/usr/local/sbin/wsl-netns.sh 1 # replace with the instance number

Or start up WSL directly with it via:

wsl ~ -d centos7-1 -u root -e sh -c "/usr/local/sbin/wsl-netns.sh 1"

Just make sure to use a unique instance number for each. The address for each instance/namespace will be 10.0.0.<instance_number>, and you can access it from any other WSL2 instance.

To test, start a simple service inside one of the namespaces. For example, if you are in the namespace that the script dropped you into, you could just run:

python3 -m http.server

You can then access that server at 10.0.0.1:8000 from another instance. You will not be able to access the service via any other IP.

Alternatively, you don't have to use nsenter to drop into the namespace. You can directly start a service inside it via:

sudo ip netns exec vnet<num> \
    service xyz start

Or whatever your bring-up command is.

Added bonus:

  • IPv6 works since you aren't leaving internal WSL2 network.

Limitations:

  • Tested in Ubuntu - May need small tweaks for CentOS.
  • You cannot access those IP addresses from Windows, just other WSL2 instances. You would need to add additional forwarding rules to handle that.
  • Currently, all communication is via address. If you want to assign hostnames, you'll need to override WSL's automatic /etc/hosts generation and define your own. Or set it up via DNS, of course.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment