Add native WSL2 VM backend for Windows#665
Conversation
The upstream trellis-cli supports Lima for macOS/Linux local development. This adds a WSL2 backend so Windows developers get the same first-class experience via `trellis vm start`. New WSL2 backend (pkg/wsl/): - Manager implementing vm.Manager using wsl.exe commands - WindowsHostsResolver for hosts file management with UAC elevation - Ubuntu rootfs registry (22.04, 24.04) - Bootstrap installs Python, Ansible, Node.js LTS, Corepack - Project files copied to ext4 for native performance (~80ms vs ~14s TTFB) - Auto-stops other trellis distros (shared network namespace) - SyncBack prompt before stopping other running distros - Breadcrumb file for cross-distro SyncBack support New commands: - vm open: Launch VS Code connected to WSL distro - vm sync: Manual WSL-to-Windows file sync - vm trust: Re-import self-signed SSL certs into Windows trust store Enhanced existing commands: - vm start/stop/delete/shell: WSL2 backend support - db open: Works from both Windows and WSL terminals - provision, deploy, vault, galaxy, xdebug-tunnel: Windows host detection with redirect to WSL terminal Other changes: - Windows os.Rename retry loop for antivirus file locks - rundll32 URI handler (fixes cmd.exe & parsing in URIs) - UTF-16LE decoder for wsl.exe output
…backend feat: Add native WSL2 virtual machine backend for Windows
# Conflicts: # README.md
The isProvisioned check relied solely on an external .provisioned marker file. Distros provisioned before the marker system was introduced (or whose marker was lost) were incorrectly identified as unprovisioned and silently deleted on the next vm start. Changes: - isProvisioned() now has a two-tier check: marker file first, then falls back to checking /etc/trellis-project-root (breadcrumb written during bootstrap) inside the distro. Self-heals the marker on success. - vm start now prompts for confirmation before deleting a distro that appears unprovisioned, instead of silently deleting it.
…fety Fix vm start silently deleting provisioned WSL distros
|
@qwatts-dev Great work and thanks for the PR! Steps I took to test on a PC: I had to install Go for Windows along with Python
Switched over to WSL:
Shouldn't this output explain to run "trellis-cli.exe"? Switched back over to PowerShell: Provision completed without issues, local site loads as expected ✅ Why is there a separate |
|
@retlehs Thanks for testing this, and great to hear provision and site loading worked! Addressing your questions:
This is expected.. Guard message.. "Shouldn't this output explain to run Good catch on the UX. The message is technically accurate ("Run this command from Windows PowerShell, not from inside WSL"), but I agree it could be clearer about what to do. I'll improve the wording to echo back the actual command to run. "Why is there a separate So, this is purely a dev/fork convenience.. The WSL distro needs a Linux build of For upstream, this would be replaced by the official install script, which already supports Linux.. bootstrap would just run it inside the distro to install the correct platform binary. No separate |
|
👍 no need to tweak the messaging to account for “trellis.exe” |
|
Do you want to make a new PR based off a branch on your fork so that @swalkinshaw and I can make direct edits without affecting your fork for now? (Make sure to allow maintainers to make edits when opening the PR) |
…stall - Add WSL guard to 'trellis init': detects WSL backend and prints a message explaining that dependencies are managed inside the VM automatically, instead of failing with a virtualenv error. - Improve windowsHostRequired() guard message: echoes back the actual command to run (e.g. "Run 'trellis vm start' from Windows PowerShell") instead of the generic "Run this command from PowerShell". - Add windowsHostRequired() guard to 'vm open' and 'vm sync': users running these from inside WSL now get the correct "run from PowerShell" message instead of a confusing "only supported on Windows (WSL2)" error. - Make CLI install in bootstrap upstream-ready: checks for a cross-compiled trellis-linux sidecar first (dev/fork builds), falls back to the official install script (scripts/get) for upstream releases. Previously, if no sidecar was found, the distro silently had no CLI binary.
Improve WSL UX: init guard, guard messages, and upstream-ready CLI install
|
Oh, I'd already pushed a few small ux tweaks before seeing your latest message.. They are:
I'll close this PR and open a new one from a branch on my fork with maintainer edits enabled. One sec... |
Summary
Adds a
wslVM backend so Windows users get a nativetrellis vmexperience using WSL2 — no nested VMs, no Vagrant, no VirtualBox.This mirrors the Lima backend's role on macOS/Linux: each Trellis project gets its own isolated Ubuntu 24.04 environment with project files on ext4, Ansible running locally inside the distro, and ports accessible directly from Windows.
Discourse thread: https://discourse.roots.io/t/native-wsl2-vm-backend-for-trellis-on-windows-looking-for-testers/30281/2
Motivation
Windows users currently have two options, both painful:
trellis vmintegration, no auto-provisioning, no config sync.WSL2 is already a VM — this backend uses it directly instead of nesting another VM inside it.
What's new
New files
pkg/wsl/manager.govm.Managerimplementation (~1150 lines)pkg/wsl/hosts.gopkg/wsl/ubuntu.gocmd/vm_open.go--folder-uri vscode-remote://wsl+<distro>/pathcmd/vm_sync.gocmd/vm_trust.goModified files
cmd/vm.gocase "wsl"innewVmManager()+ two guard functionscmd/vm_start.gocmd/vm_stop.gocmd/vm_delete.gowindowsHostRequired()guardcmd/vm_shell.gowindowsHostRequired()guardtrellis/trellis.goVmManagerType()returns"wsl"on Windows, WSL auto-detection,CheckVirtualenvskipcmd/db_open.gomysql://URI for WSL (no SSH tunnel needed)pkg/db_opener/tableplus.gorundll32.exeURI opening for Windowsgithub/main.goos.Rename(Windows antivirus file locks)cmd/provision.go,cmd/deploy.go, etc.wslTerminalRequired()guard on Ansible commandsmain.govm open,vm sync,vm trustcommandsDesign decisions
wsl.Managerimplementsvm.Manageridentically tolima.Manager. All WSL-specific code lives inpkg/wsl/— no Windows logic scattered elsewhere.VmManagerType()returns"wsl"whenruntime.GOOS == "windows"and the manager is"auto". No user configuration needed.trellis-<site>). Projects are rsync'd to ext4 at/home/admin/<project>/.ansible_connection=localwithansible_user=admin. No SSH keys, no tunnels.wslTerminalRequired()— redirects Ansible commands from Windows → "runtrellis vm openfirst"windowsHostRequired()— redirects VM management from WSL → "run this from Windows"StartInstanceprompts to sync and stop other runningtrellis-*distros.syncConfigFromWSL()rsyncsgroup_vars/from ext4→Windows on manager init, keeping the Windows-side repo current.Testing
Tested on Windows 11 with WSL2:
trellis new) — full bootstrap + provision + site loadsvm start/stop/shell/open/deletelifecycleprovision,deploy,db opencommandsPre-built binaries available for hands-on testing: https://github.com/qwatts-dev/trellis-cli/releases/tag/v1.18.0-wsl2.1
Checklist
go vet ./...passescommandpackage for exec,colorfor output,promptuifor prompts)runtime.GOOS == "windows"orWSL_DISTRO_NAMEchecks)