Require WSL version 2.5.1 or later

Podman machines v6.x don't start on versions of WSL earlier than v2.5.1
because of their old Kernels with cgroupv1 support.

Signed-off-by: Mario Loriedo <mario.loriedo@gmail.com>
(cherry picked from commit f999886ce2)
This commit is contained in:
Mario Loriedo committed 2026-11-05 19:01:46 +01:00
1 parent 375bd63144
commit 421a07d834
6 files changed
+149 -6

No files matched your search

+1 -1
View File
@@ -115,7 +115,7 @@ the following command, from a PowerShell or Windows Command Prompt terminal in
wsl --install
```
For more information refer to
Podman requires WSL v2.5.1 or later. For more information refer to
[the official documentation](https://learn.microsoft.com/en-us/windows/wsl/).
#### Hyper-V
+2
View File
@@ -53,6 +53,8 @@ later. Internally, WSL and Hyper-V use virtualization, so your system must
support and have hardware virtualization enabled. If you are running Windows on
a VM, you must have a VM that supports nested virtualization.
WSL v2.5.1 or later is required.
Hyper-V is only available on Windows Enterprise, Pro, or Education editions (not
Home). The command to initialize the first Hyper-V Podman machine, and the command
to remove the last one, both require administrator privileges because they create
+16
View File
@@ -119,6 +119,22 @@ func provisionWSLDist(name string, imagePath string, prompt string) (string, err
return dist, nil
}
func verifyWSLVersion(rootful bool) error {
// WSL v2.5.1 or later is required. Earlier versions will fail to
// start Podman machines because they supported cgroupv1.
if !wutil.WSLVersionAtLeast(2, 5, 1) {
return fmt.Errorf("WSL version 2.5.1 or later is required to run Podman. Please upgrade WSL with: wsl --update")
}
// WSL v2.7.5 or later is required in rootful mode. Earlier versions
// don't support nftables:
// https://github.com/podman-container-tools/podman/issues/25201
if rootful && !wutil.WSLVersionAtLeast(2, 7, 5) {
return fmt.Errorf("WSL version 2.7.5 or later is required to run Podman in rootful mode. Please use rootless mode or upgrade WSL with: wsl --update")
}
return nil
}
func createKeys(mc *vmconfigs.MachineConfig, dist string) error {
user := mc.SSH.RemoteUsername
+7
View File
@@ -35,6 +35,10 @@ func (w WSLStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConf
_ = setupWslProxyEnv()
if err = verifyWSLVersion(mc.HostUser.Rootful); err != nil {
return err
}
if opts.UserModeNetworking {
if err = verifyWSLUserModeCompat(); err != nil {
return err
@@ -134,6 +138,9 @@ func (w WSLStubber) SetProviderAttrs(mc *vmconfigs.MachineConfig, opts define.Se
}
if opts.Rootful != nil && mc.HostUser.Rootful != *opts.Rootful {
if err = verifyWSLVersion(*opts.Rootful); err != nil {
return err
}
if err := mc.SetRootful(*opts.Rootful); err != nil {
return err
}
+70 -5
View File
@@ -9,6 +9,8 @@ import (
"io"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"sync"
"syscall"
@@ -19,6 +21,8 @@ import (
var (
onceStatus sync.Once
status wslStatus
onceVersion sync.Once
version wslVersion
wslNotInstalledMessages = []string{"kernel file is not found", "The Windows Subsystem for Linux is not installed"}
vmpDisabledMessages = []string{"enable the Virtual Machine Platform Windows feature", "Enable \"Virtual Machine Platform\""}
wslDisabledMessages = []string{"enable the \"Windows Subsystem for Linux\" optional component"}
@@ -30,6 +34,12 @@ type wslStatus struct {
wslFeatureEnabled bool
}
type wslVersion struct {
val [3]int
cmdErr error
parseErr error
}
func NewWSLCommand(arg ...string) *exec.Cmd {
cmd := exec.Command("wsl", arg...)
cmd.Env = append(os.Environ(), "WSL_UTF8=1")
@@ -88,14 +98,69 @@ func IsWSLInstalled() bool {
}
func IsWSLStoreVersionInstalled() bool {
cmd := SilentExecCmd("--version")
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
// The Windows built-in version of WSL returns an error when running
// `wsl --version`. Recent versions of WSL instead, are distributed
// via the Microsoft store and supports `wsl --version`.
return parseWSLVersion().cmdErr == nil
}
// WSLVersionAtLeast returns true if the installed WSL version is >= major.minor.patch
// or if it fails to parse the wsl --version output. It returns false otherwise and if
// the `wsl --version` command has failed.
func WSLVersionAtLeast(major, minor, patch int) bool {
v := parseWSLVersion()
// When the `wsl --version` command fails we return false
// because it's older WSL versions behavior.
if v.cmdErr != nil {
logrus.Debugf("command error while checking the WSL version: %v", v.cmdErr)
return false
}
// When there is a parsing error we assume that's a WSL future version
// where the output format has been changed.
if v.parseErr != nil {
logrus.Debugf("parsing error while checking the WSL version: %v", v.parseErr)
return true
}
return v.val[0] > major ||
(v.val[0] == major && v.val[1] > minor) ||
(v.val[0] == major && v.val[1] == minor && v.val[2] >= patch)
}
return true
// parseWSLVersion returns the WSL version as [major, minor, patch] in the
// returned wslVersion struct.
// The returned `wslVersion.cmdErr` is non nil if the `wsl --version` command fails.
// The returned `wslVersion.parseErr` is non nil if the major.minor.patch string
// cannot found in the returned output.
func parseWSLVersion() wslVersion {
onceVersion.Do(func() {
cmd := SilentExecCmd("--version")
out, err := cmd.Output()
if err != nil {
version = wslVersion{cmdErr: fmt.Errorf("running wsl --version failed: %w", err)}
return
}
version = versionFromLocalizedOutput(string(out))
})
return version
}
// versionFromLocalizedOutput returns the WSL version from the output of "wsl --version".
// The output labels are localized, so the version is the first version number
// in the output, that is the WSL package version in all languages.
func versionFromLocalizedOutput(out string) wslVersion {
v := wslVersion{}
versionRegexp := regexp.MustCompile(`(\d+)\.(\d+)\.(\d+)`)
m := versionRegexp.FindStringSubmatch(out)
if m == nil {
return wslVersion{parseErr: fmt.Errorf("could not parse WSL version from %q", out)}
}
for i := range v.val {
var err error
if v.val[i], err = strconv.Atoi(m[i+1]); err != nil {
return wslVersion{parseErr: fmt.Errorf("could not parse the WSL version digit %q", m[i+1])}
}
}
return v
}
func matchOutputLine(output io.ReadCloser) (wslStatus, error) {
+53
View File
@@ -150,3 +150,56 @@ func TestNewWSLCommand(t *testing.T) {
assert.Equal(t, []string{"--status"}, cmd.Args[1:])
assert.Contains(t, cmd.Env, "WSL_UTF8=1")
}
func TestVersionFromLocalizedOutput(t *testing.T) {
tests := []struct {
name string
output string
wantVersionVal [3]int
wantParseErr bool
}{
{
"English",
"WSL version: 2.6.1.0\nKernel version: 6.6.87.2-1\nWSLg version: 1.0.66\nWindows version: 10.0.26100.4652\n",
[3]int{2, 6, 1},
false,
},
{
"German",
"WSL-Version: 2.5.10.0\nKernelversion: 6.6.87.2-1\nWSLg-Version: 1.0.66\nWindows-Version: 10.0.26100.4652\n",
[3]int{2, 5, 10},
false,
},
{
"Japanese",
"WSL バージョン: 3.0.3.0\nカーネル バージョン: 6.6.87.2-1\nWSLg バージョン: 1.0.66\nWindows バージョン: 10.0.26100.4652\n",
[3]int{3, 0, 3},
false,
},
{
"CRLF line endings",
"WSL version: 2.5.1.0\r\nKernel version: 6.6.87.2-1\r\n",
[3]int{2, 5, 1},
false,
},
{
"No version",
"Invalid command line option: --version\n",
[3]int{},
true,
},
{
"Empty",
"",
[3]int{},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := versionFromLocalizedOutput(tt.output)
assert.Equal(t, tt.wantVersionVal, v.val)
assert.Equal(t, tt.wantParseErr, v.parseErr != nil)
})
}
}