Skip to content

How MSL works

MSL is built the way WSL 2 is: one lightweight Linux VM runs every distribution, and each distribution is an isolated set of processes inside it rather than a VM of its own. The VM runs on Apple’s Virtualization.framework, with no other hypervisor or kernel extension.

The parts

msl (command)  ──▶  msld (background service, one per macOS user)
                      │  runs the VM; sets up sessions, forwarded ports, ~/.msl/distros
                      │  ├─ msl-portd      carries forwarded localhost connections
                      │  └─ msl-fileviewd  carries ~/.msl/distros
                      ▼
               Linux VM (MSL kernel)
                 ├─ Ubuntu   (its own init and processes)
                 └─ Debian   (its own init and processes)
  • msl is the command you run. It takes wsl.exe’s arguments and passes each request to msld. A command’s input and output go straight between msl and the VM: msld starts the command and reports its exit code but doesn’t carry its data, so a large or stuck command doesn’t slow down the others.
  • msld is a background process that runs as your macOS user. It’s a LaunchAgent that launchd starts when the first msl command connects, and it stays until you log out or msl --update replaces it. It isn’t a login item, and it needs no administrator rights. When you log out, restart or shut down the Mac, msld stops the distributions, unmounts and flushes their disks, and powers the VM off before it exits; launchd gives it up to 30 seconds.
  • msl-portd carries the connections to forwarded ports, like WSL’s wslrelay.exe. msld starts it when the first port is forwarded, and it exits after the last one closes.
  • msl-fileviewd carries the files of ~/.msl/distros between macOS’s NFS client and the VM, and checks each request. It runs while the VM does.
  • The VM runs MSL’s Linux kernel. msld starts it when a distribution first needs it.
  • Each distribution runs inside the VM with its own init: MSL’s own, or systemd if you turn it on.

What distributions share

Distributions are isolated from each other with Linux namespaces, as in WSL 2.

Shared by all distributionsSeparate for each distribution
The kernel, memory and processorsThe file system: its own disk, ext4.img, and its own mounts
The network: one IP address and one localhostProcesses and process IDs
The macOS file system at /mnt/macosHostname
Disks attached with msl --mount, at /mnt/mslControl groups, and init (systemd or MSL’s)

Because a distribution is a set of namespaces rather than a VM, it starts in milliseconds once the VM is up. Isolation between distributions is the same as in WSL 2: good enough to keep them from getting in each other’s way, but weaker than separate VMs.

Every distribution’s disk (up to 19) is attached to the VM when it starts, and Virtualization.framework serves it, so msld never handles disk I/O. Virtualization.framework can’t add a disk to a running VM: a disk that appears while the VM runs makes msld restart the VM if no distribution is running, and otherwise the VM mounts it through the Mac file share until it next restarts. See Manage disk space.

Starting and stopping

Everything starts on demand and stops when it’s idle:

  1. The first msl command starts msld, which starts the VM and then the distribution.
  2. A distribution stops 15 seconds after its last msl session ends: a shell, a command, or a VS Code connection (instanceIdleTimeout). Services running inside don’t keep it up.
  3. The VM stops 60 seconds after the last distribution does (vmIdleTimeout).

msl -t <distro> stops one distribution now, and msl --shutdown stops all of them and the VM. Both timeouts are set in ~/.mslconfig. See Advanced settings configuration.

Memory

The VM gets 50% of the Mac’s memory by default (memory in ~/.mslconfig). Memory the VM has used goes back to macOS only when the VM stops, not while it runs: Virtualization.framework doesn’t return freed guest memory to macOS (#37). WSL’s autoMemoryReclaim is accepted and has no effect.

In practice, the idle timeouts return memory for you: when you stop using MSL, the VM stops a minute or so later. To get it back now, run msl --shutdown.

The kernel

MSL runs its own build of Linux, from msl-kernel, bundled with each release. msl --version shows which one.

The kernel uses 16 KiB memory pages, like the Mac itself, where most Arm Linux systems use 4 KiB. With 4 KiB pages, a Virtualization.framework bug corrupts the VM’s memory when macOS runs short of memory (#48). Distribution packages work with 16 KiB pages; a program built on the assumption of 4 KiB pages may not. getconf PAGESIZE prints 16384. See Troubleshooting.

With nestedVirtualization on (the default) and a Mac with an M3 chip or later, the VM can run virtual machines of its own: MSL’s kernel has KVM built in, and /dev/kvm exists in every distribution.

The VM keeps one machine identifier across boots, and its own /etc/machine-id is that identifier’s UUID. Distributions keep their own /etc/machine-id.

kernel in ~/.mslconfig boots a kernel of your own instead.

Where MSL keeps its state

WhatWhere on macOS
Distributions’ disks~/Library/Application Support/msl/distros/<id>/ext4.img, or the location you chose
The list of distributions, the machine identifier, logs and sockets~/Library/Application Support/msl/
Distribution files, while the VM runs~/.msl/distros/<distro>
Downloaded images and the VS Code Server~/Library/Caches/msl/
VM settings~/.mslconfig, if you create it
MSL itself~/.local by default, or the prefix you installed to

msl --uninstall removes MSL itself and keeps your distributions and settings. See Update and uninstall MSL.

Last updated on