Skip to content
WSLDeep Dive Published Updated 5 min readViews unavailable

WSL 2 and cgroup v2: Resource Control Inside the Linux VM

Separate Windows VM limits from Linux cgroups, enable or verify the WSL cgroup hierarchy, and avoid confusing resource control with isolation.

WSL 2 has two layers of resource management that are easy to conflate. The Windows-side WSL virtual machine has an overall memory, processor, and swap budget. Inside that Linux VM, cgroups account for and control resources assigned to services, user sessions, and containers. A Linux container cannot use more CPU or memory than the VM can obtain from Windows, but the VM’s global budget does not by itself define how competing Linux workloads should share that capacity.

On recent WSL versions, cgroup v2 is the documented default hierarchy. WSL’s per-distribution configuration also exposes a cgroups setting that selects v1 or v2. systemd uses cgroups to track services and scopes, while container runtimes create their own child groups. Understanding which layer owns a limit makes resource incidents much easier to diagnose.

VM ceilings and Linux allocations

The global .wslconfig file controls the WSL 2 VM as a whole. Its memory, processor, and swap settings define the outer budget across WSL 2 distributions. The per-distribution /etc/wsl.conf file controls Linux-side behavior, including the documented cgroup hierarchy selection. Changing one layer does not automatically configure the other.

Inside cgroup v2, the unified hierarchy presents controller files under a mounted cgroup filesystem, commonly /sys/fs/cgroup. Controllers can expose accounting and limits for CPU, memory, process count, and other resources. systemd places services and scopes in groups and can apply properties such as MemoryMax or CPUQuota to a unit. Container runtimes create nested groups so that a container’s resource policy can be inspected separately from the host VM budget.

These limits are hierarchical. A child group cannot receive more capacity than its ancestors permit. Setting a generous container memory limit does not enlarge the WSL VM, and lowering the VM memory budget can cause pressure across every distribution even when each container appears to have its own limit.

Configure the hierarchy carefully

Microsoft documents cgroups as a per-distribution WSL setting with v1 and v2 values and v2 as the default. A configuration fragment is:

# /etc/wsl.conf
[automount]
cgroups=v2

[boot]
systemd=true

Use the exact section and options supported by the installed WSL release; the snippet combines the documented cgroups setting with systemd enablement. Changing the hierarchy can affect systemd, container runtimes, and tools that expect particular controller files. Do not switch a working development distribution from v2 to v1 merely to make one old script pass without understanding the dependency.

After editing wsl.conf, the distribution must stop and restart before the change is applied. From PowerShell, wsl –shutdown stops all running WSL instances, not just the current terminal, so save work and schedule the restart when that interruption is acceptable. If the goal is to restart only one distribution, wsl –terminate with its name has a narrower scope.

Verify what is actually mounted

Do not infer the active hierarchy from a configuration file alone. Confirm the WSL version and distribution state from Windows, then inspect the Linux mount and controller files:

findmnt -T /sys/fs/cgroup -o TARGET,FSTYPE,OPTIONS
cat /proc/self/cgroup
cat /sys/fs/cgroup/cgroup.controllers
systemctl is-system-running
systemctl show -p ControlGroup -p MemoryMax -p CPUQuota example.service

On a cgroup v2 system, the cgroup filesystem should be mounted as cgroup2 and expose unified controller files such as cgroup.controllers. The exact controller list depends on the kernel and its configuration. A service may report an unlimited property even though its parent group or the WSL VM itself has a tighter ceiling, so inspect the ancestor path as well as the unit.

For a systemd-managed service, a runtime limit can be applied with a property command such as systemctl set-property –runtime example.service MemoryMax=1G CPUQuota=200%. Use a real unit, verify the properties with systemctl show, and check the unit’s cgroup files. A 200% CPU quota represents up to two CPU-equivalents of runtime under systemd’s quota semantics; it is not a promise of two dedicated cores.

Limits are not a security boundary

Cgroups are resource-accounting and control mechanisms. They do not create a complete security boundary by themselves. Process namespaces, filesystem permissions, capabilities, syscall filtering, device access, and the runtime’s mount setup are separate concerns. A process that can write to a privileged parent cgroup may be able to change resource policy, so delegation and ownership must be controlled.

The Linux cgroup v2 documentation describes delegation rules and the importance of not granting child managers write access to the parent’s controller files. This matters for nested runtimes and service managers: a container can manage its own child groups only when the parent has enabled and delegated the relevant controllers correctly.

Diagnose pressure from the outside in

When a process is slow or killed, inspect both resource layers. First check the Windows VM budget and whether another distribution or workload is consuming it. Then inspect the Linux cgroup path, memory.current and memory.events for the affected group, CPU throttling counters, and systemd unit properties. For container workloads, compare the container’s configured limit with its parent group and with the total WSL VM budget.

A cgroup OOM event inside Linux and a Windows-side VM memory ceiling are different failure paths. The symptom may look like an application being killed in both cases, but the logs and counters differ. Preserve the relevant cgroup files, systemd properties, WSL version, .wslconfig and wsl.conf, and host memory observations before changing limits.

The key operational model is nested control: Windows limits the VM; Linux cgroups subdivide what the VM can use. Diagnose and size each layer independently, and do not mistake a visible cgroup hierarchy for proof that a workload is isolated or correctly bounded.

Related:

Sources:

Comments