WSL
Configures Windows Subsystem for Linux with wslu utilities and sensible defaults.
What it does
- Installs the wslu utilities package
- Configures
/etc/wsl.conf(systemd, automount, interop) - Manages the Windows-side
%USERPROFILE%\.wslconfig(global WSL VM settings), shipping memory-reclaim and sparse-VHDX performance defaults you deep-merge over - Sets
BROWSER=wslviewandWIN_HOMEpointing to the Windows user home - Registers
wslfetchinversions.d - Shell aliases for opening files, clipboard, and common Windows folders
Variables
| Variable | Default | Description |
|---|---|---|
wsl_config | see defaults | /etc/wsl.conf settings, by section; deep-merges over the shipped defaults |
wsl_config_template | wsl.conf.j2 | Template rendered (through Jinja) to /etc/wsl.conf; point at your own to render a config from vars |
wsl_config_file | "" | Static file copied verbatim to /etc/wsl.conf, no Jinja; when set it wins over the template and dict |
wsl_wslconfig | see defaults | Windows-side %USERPROFILE%\.wslconfig settings, by section; deep-merges over the shipped performance defaults |
wsl_wslconfig_template | wslconfig.j2 | Template rendered to .wslconfig; point at your own to render a config from vars |
wsl_wslconfig_file | "" | Static file copied verbatim to .wslconfig, no Jinja; when set it wins over the template and dict |
wsl_windows_shims | see defaults | Commands to shim into %USERPROFILE%\bin\ |
wsl_wslenv | see defaults | Env vars bridged into WSL via WSLENV (default flag /u) |
Config (defaults)
This is the default application config the role ships for WSL, written to /etc/wsl.conf. With hash_behaviour = merge, your wsl_config overrides deep-merge onto these defaults, so you change only the keys you set. Each top-level key is a wsl.conf section and its keys are rendered verbatim (booleans lowercased to true/false). The full set of sections and keys is documented in Microsoft's wsl.conf reference:
| Setting | Value | Effect |
|---|---|---|
boot.systemd | true | Boot WSL with systemd as PID 1, so systemctl, Docker, snap and other unit-based services work as on a normal Linux box |
interop.appendWindowsPath | false | Keep the Windows PATH out of WSL, so Linux tools take precedence and lookups stay fast without scanning hundreds of Windows entries |
automount.options | "metadata" | Mount Windows drives with metadata so Linux ownership and permissions persist on /mnt/c files |
Rendered, the defaults produce this /etc/wsl.conf:
[boot]
systemd = true
[interop]
appendWindowsPath = false
[automount]
options = "metadata"Examples
Override only the wsl.conf keys you care about; everything else falls through to the shipped defaults. Add new sections or keys the role doesn't ship by default the same way:
wsl_config:
interop:
appendWindowsPath: true # default: false
network:
generateResolvConf: false # section not shipped by defaultShipping a fixed config file
If you'd rather hand-maintain a complete wsl.conf than build it from wsl_config, there are two ways in, depending on whether you want Jinja.
For a config copied verbatim (no rendering), set wsl_config_file to a file in your playbook's files/ dir (or an absolute path):
wsl_config_file: wsl.confFor a config you want rendered through Jinja (so it can reference vars), point wsl_config_template at your own template in your playbook's templates/ dir:
wsl_config_template: my-wsl.conf.j2 # default: wsl.conf.j2Either way wsl_config is ignored; if both are set, wsl_config_file wins.
Don't reuse the shipped template name
For the template path, the role's own templates/ is searched before your playbook's, so a file named wsl.conf.j2 is shadowed by the shipped template and never picked up. Give yours a different name. (The verbatim wsl_config_file path uses files/, so it has no such clash.)
.wslconfig (the Windows side)
wsl.conf configures one distro from inside; %USERPROFILE%\.wslconfig is its Windows-side counterpart, the global settings for the WSL 2 VM itself (memory and processor limits, swap, networking mode). Same section/key structure, documented in the same Microsoft reference.
The role ships two machine-independent performance defaults under [wsl2], and your wsl_wslconfig deep-merges over them (hash_behaviour = merge), so you add the machine-specific caps without redeclaring these:
| Setting | Value | Effect |
|---|---|---|
wsl2.autoMemoryReclaim | gradual | Hand cached memory back to Windows as WSL frees it, instead of the VM holding its peak footprint until wsl --shutdown |
wsl2.sparseVhd | true | Let the ext4 VHDX shrink as you delete data, instead of only ever growing (an old VHDX never reclaims freed space) |
The memory, processor, and swap caps are machine-specific (they depend on the host's RAM and core count), so set them per host in your inventory rather than here. They deep-merge onto the defaults above:
wsl_wslconfig: # default: autoMemoryReclaim + sparseVhd under wsl2
wsl2:
memory: 24GB # cap the VM's RAM, leaving headroom for Windows
processors: 12 # leave a few cores for the host
swap: 8GB
networkingMode: mirrored # optional: localhost parity, can affect VPNsTo hand-maintain the whole file instead, the wsl_wslconfig_template / wsl_wslconfig_file escape hatches work exactly like their wsl_config counterparts above (including the shipped-name shadowing warning, wslconfig.j2); either one bypasses the dict and the shipped defaults entirely.
Shim extra Windows-facing commands and bridge more variables into WSL. Each list replaces its default, so include the entries you want to keep:
wsl_windows_shims: # default: [gke-gcloud-auth-plugin, gcloud, kubectl, helm, doctl]
- gke-gcloud-auth-plugin
- gcloud
- kubectl
- helm
- terraform
wsl_wslenv: # default: [CLOUDSDK_CORE_ACCOUNT, CLOUDSDK_ACTIVE_CONFIG_NAME, DIGITALOCEAN_CONTEXT]
- CLOUDSDK_CORE_ACCOUNT
- CLOUDSDK_ACTIVE_CONFIG_NAME
- KUBECONFIG/up # /up flag: a path that needs Windows-to-WSL translationAliases
| Alias | Command | Description |
|---|---|---|
open | wslview | Open a file or URL with Windows |
copy | clip.exe | Copy stdin to the Windows clipboard |
desktop | cd ... | Navigate to the Windows Desktop folder |
downloads | cd ... | Navigate to the Windows Downloads folder |
documents | cd ... | Navigate to the Windows Documents folder |
Windows shims
Windows-native tools like Lens run exec plugins (e.g. gke-gcloud-auth-plugin for GKE token refresh) from the Windows PATH, not from inside WSL. This role creates .cmd shim files in %USERPROFILE%\bin\ for each command listed in wsl_windows_shims that is detected on the WSL PATH. Each shim delegates the call to the WSL default distro:
@echo off
rem WSL shim — delegates %~n0 to the WSL default distro. %~n0 expands to the filename without extension.
rem Sources ~/.profile.d/googlecloud.sh to add gcloud to PATH. WSLENV bridges credential env vars from Windows.
rem Managed by Ansible (dotfilespro.roles wsl role).
wsl bash -c ". ~/.profile.d/googlecloud.sh 2>/dev/null; %~n0 %*"%~n0 expands to the filename without extension, so a single template covers all shims. %USERPROFILE%\bin\ is created automatically and added to the Windows user PATH if not already present.
Commands not found in WSL are silently skipped, and managed shims whose command was removed from wsl_windows_shims are deleted on the next run (only .cmd files carrying the managed-by-Ansible marker are touched).
Shims run in a non-login shell, so they source only ~/.profile.d/googlecloud.sh (deployed by the googlecloud role) for the gcloud PATH, so interactive configuration never leaks into background invocations.
Command detection sources the same file, so a shim is only created when the command resolves in the exact environment the shim will run in. Roles that provide shimmed commands should run before this role.
WSLENV
When a Windows process (e.g. Lens) invokes a shim, the environment variables set in the kubeconfig exec.env stanza (such as CLOUDSDK_CORE_ACCOUNT) live only in the Windows process. By default wsl starts a fresh Linux environment and does not inherit those Windows variables.
WSLENV is a Windows mechanism that explicitly bridges named variables from Windows into WSL. This role configures each var in wsl_wslenv with the /u flag (only included when invoking WSL from Win32) in the Windows user environment:
WSLENV=CLOUDSDK_CORE_ACCOUNT/u:CLOUDSDK_ACTIVE_CONFIG_NAME/uEntries may carry their own WSLENV flags (e.g. KUBECONFIG/up for a path that needs translation); plain names default to /u. Existing entries with the same variable name are replaced, so flag changes propagate on the next run.
With this in place, when Lens calls the shim with CLOUDSDK_CORE_ACCOUNT set (from the kubeconfig exec env), it flows through wsl into the bash process so gcloud authenticates as the correct account.
The gcp-kubeconfig function in the Google role embeds CLOUDSDK_CORE_ACCOUNT into the kubeconfig exec env stanza automatically after gcloud container clusters get-credentials.
References:
- Share environment variables between WSL and Windows (WSLENV)
- How to manage Kubernetes clusters in WSL with OpenLens on Windows
- Using WSL and Lens — Lens Forums
- GKE cluster connect error: gke-gcloud-auth-plugin not found — Lens Forums
wsl.conf and .wslconfig changes need a full WSL restart
Changes to /etc/wsl.conf or %USERPROFILE%\.wslconfig require a full WSL restart to take effect:
wsl --shutdownTroubleshooting
A shim didn't appear in %USERPROFILE%\bin\
Shims are only created for commands that resolve on the WSL PATH in the exact environment the shim runs in (a non-login shell sourcing ~/.profile.d/googlecloud.sh). If a command isn't shimmed, the role that provides it either ran after the wsl role or isn't installed yet.
Ensure the providing role (e.g. googlecloud, kubernetes) runs before wsl, then re-apply. Commands not found are silently skipped, so check the command works in a fresh shell first: bash -c '. ~/.profile.d/googlecloud.sh; command -v gcloud'.
Lens still can't authenticate to GKE
The shim runs, but gcloud authenticates as the wrong account or fails entirely. This means the credential variable isn't bridged into WSL.
Confirm the variable is listed in wsl_wslenv (so it lands in the Windows WSLENV with the /u flag) and that gcp-kubeconfig embedded CLOUDSDK_CORE_ACCOUNT into the kubeconfig exec.env stanza. WSLENV changes are read when a new Windows process starts, so restart Lens after re-applying.
Changes to mounts, interop, or the default user did nothing
These live in /etc/wsl.conf and need a full WSL restart (wsl --shutdown) to take effect, per the warning above. A new terminal alone is not enough.