Import and Run Cloud Containers in WSL

Some cloud image containers provided by Linux distros can be directly imported into Windows Subsystem for Linux.

Networking

Unfortunately Windows Subsystem is designed so that all containers share the same network configuration. This means that if you configure an interface on one instance, that interface will apply to all other WSL containers.

This makes WSL unsuitable for running test networks. There is a hack that involves switching to bridged networking mode, using a switch in external mode, and running a script in each container that sets up a unique IP address, but I’m not sure it’s worth it. If you really want a test network you might be better off with VMWare or Hyper-V.

That said, bridged networking is kind of cool. To set up bridged networking please see the Creating Virtual Switch in Hyper-V Manager section in this guide.

If you use bridged network make sure to enable systemd in the container’s /etc/wsl.conf and configure networking the way you usually would in the Linux host, for example with systemd-networkd, netplan.io, NetworkManager, ifupdown, etc.

If you follow the guide, you don’t actually have to create a new switch named WSL_External, you can just change Connection type for the default WSL switch from internal to external. Make sure WSL is shutdown before doing this. Finally, be sure to update .wslconfig, changing networkingMode to bridged and and vmSwitch to WSL (or WSL_External if you created that).

You can use the script in this post to enable each container to have its own unique IP address. Without this script all containers will share the same the IP address.

One thing to note: When you create /usr/local/sbin/wsl-netns.sh in each container as per the post above, remember to chmod +x it otherwise the container will fail to start. The idea of appending -u root -e sh -c "/usr/local/sbin/wsl-netns.sh 1" to the Command line of each container, using the final integer as the local part of the IP address, is a brilliant.

Also remember you have to wsl -t each container after you add the script and change its Command line setting.

A final obstacle to WSL being useful for running test networks is when containers are not open in Windows Terminal they do not run in the background.

As a solution for running a network of lightweight containers, systemd’s containerd|nspawn|machined on Linux is a much more robust solution where you can run custom, lightweight mmdebstrap or other images.

Obtain Cloud Images

Linux distributions provide online download repositories for their cloud images. Here are some examples:

Ubuntu 24.04 LTS (Noble): https://cloud-images.ubuntu.com/noble/current/

Rocky Linux 9 x86_64: https://dl.rockylinux.org/pub/rocky/9/images/x86_64/

Look for .tar.xz images. Not all .tar.xz images may work with WSL, but I have found that ones from Ubuntu and Rocky tend to while Debian may have some issues.

For this example I used Ubuntu’s noble-server-cloudimg-amd64-root.tar.xz and Rocky Linux’ Rocky-9-Container-UBI.latest.x86_64.tar.xz. Note that there is a Rocky-9-Container-Minimal.latest.x86_64.tar.xz image available but it is so minimal that it doesn’t contain yum and dnf which makes setting it up under WSL difficult.

I also tried debian-12-genericcloud-amd64.tar.xz from https://cloud.debian.org/images/cloud/bookworm/latest/ however it didn’t run when imported into WSL.

Import Image

In an administrator PowerShell import the image:

wsl --import <name> "C:\Users\user\Virtual Machines\<name>" "D:\Downloads\ISO\Rocky-9-Container-UBI.latest.x86_64.tar.xz"

Change <name> to whatever you want to name your WSL container and adjust the paths to your virtual machine directory and the path to the downloaded container .tar.xz.

The import process should finish and output the following:

Import in progress, this may take a few minutes.
The operation completed successfully.

Initial Configuration of the Container

For the Rocky container some critical components need to be added right away. Still in the administrator PowerShell from above:

wsl -d <name> --shell-type login -u root

Now install some critical components:

# procps-ng and iptables needed for the network script mentioned above
dnf install passwd sudo iproute dhcp-client less procps-ng iptables

Next add your regular user:

useradd <user>
# also add the user to the 'wheel' group for sudo privileges
# for Debian-based distros the group should be 'sudo'
usermod -G wheel <user>

Finally, set a password for both the user and for root:

# set root's password
passwd
# set user's password
password <user>

Create WSL Profile for Container

In Windows Terminal choose Settings. At the very bottom of the settings tree on the left side select + Add a new profile.

Rather than go through the trouble of creating a new profile from scratch, if you already have an existing WSL container profile you can choose the Duplicate a profile option. Then all you have to do is change Name and Command line. You should use the name that you provided above when importing the image. Click Save to save the profile.

One quirk with Windows Terminal is that when you save the profile, it creates a second profile with the same name. However this second profile should not be used. You will notice that in the second profile the value for Starting directory will default to %userprofile% which we do not want. Delete this profile by scrolling to the bottom and selecting Delete.

Create /etc/wsl.conf

We need to create /etc/wsl.conf in the new profile. Now that a profile has been added to Windows Terminal you should be able to select it from the drop-down arrow on the tab bar to open it.

It will open a shell as root. Open an editor such as vi to create /etc/wsl.conf:

vi /etc/wsl.conf

Paste the following in it:

[boot]
systemd=true

# Automatically mount Windows drive when distribution is launched
[automount]
# true: automount fixed drives (C:/ or D:/) with DrvFs under root directory set above
# false: drives won't be mounted automatically, need to be mounted manually or with fstab.
enabled = true
# process /etc/fstab when WSL distro is launched
mountFsTab = false
options = "metadata,uid=1000,gid=1000,umask=0022,fmask=11,case=off"
crossDistro = true

[filesystem]
umask = 022

[network]
hostname = <name>
# whether to generate /etc/hosts
generateHosts = false
# whether to generate /etc/resolv.conf
generateResolvConf = true

[interop]
# support launching Windows processes
enabled = false
# append windows %Path% at end of linux $PATH
appendWindowsPath = false

[user]
default=<user>

Change the values for <hostname> and <user>. [Note you don’t have to enable systemd however I like to.]

Restart the Container

In the administrator PowerShell terminate the container:

wsl -t <name>

Now restart the container by opening it from Windows Terminal again. You should notice that now it launches as your user, not as root.

If you type ip -a you should see that it has automatically obtained an IP address.

Final Notes

You can repeat this process for as many containers as you like. Note that the Ubuntu container doesn’t require installing the critical packages after being imported, but it does still require creating the user and setting the passwords.

These lightweight containers are a great way to run test networks.

If you make a mistake creating a container or just want to remove it, you can easily do so from an administrator PowerShell:

# terminate container
wsl -t <name>
# completely remove container
wsl --unregister <name>

This will completely remove the imported container. You will still need to manually delete the profile in Windows Terminal.