- Basic knowledge of PowerShell (pwsh) and *sh (Bash) in case of any unaccounted issues along the way.
(>
is PowerShell, $
is *sh root, %
is *sh user, #
are comments)
-
Setting up and downloading
# Create directory to store the WSL files > Set-Location (New-Item .\AlpineWSL -ItemType Directory).FullName # Download wsldl executable and the minirootfs > iwr 'https://github.com/yuk7/wsldl/releases/download/23051400/icons.zip' -OutFile 'icons.zip' > iwr 'https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/alpine-minirootfs-3.20.3-x86_64.tar.gz' -OutFile 'rootfs.tar.gz'
-
Extract Alpine.exe from icons.zip
# Initialize a `Shell.Application` COM object > $shell = New-Object -ComObject Shell.Application # Extract only Alpine.exe from all the files in icons.zip > $shell.NameSpace("$PWD").CopyHere($shell.NameSpace("$PWD\icons.zip").ParseName("Alpine.exe"))
While these two commands may seem complex, they're not too hard to understand. I'll break them down step-by-step in the comments.
-
Delete icons.zip and install Alpine Linux into WSL
# Remove the icons.zip file > Remove-Item .\icons.zip # Install Alpine using the extracted Alpine.exe and the downloaded minirootfs > .\Alpine.exe install .\rootfs.tar.gz # Check if the installation worked > wsl -l Windows Subsystem for Linux Distributions: Ubuntu (Default) Alpine
Great! Now we can open it up and make this minirootfs a bit less minimal.
-
Initial setup There are some things we need to set up. The setup scripts are unavailable by default in minirootfs, so we need to download them.
# First, let's check who we are $ whoami root # Now, let's make sure we can connect to the internet $ ping -c 3 gnu.org PING gnu.org (209.51.188.116): 56 data bytes 64 bytes from 209.51.188.116: seq=0 ttl=55 time=207.226 ms 64 bytes from 209.51.188.116: seq=1 ttl=55 time=212.126 ms 64 bytes from 209.51.188.116: seq=2 ttl=55 time=206.606 ms
Good, we are currently root and connected to the internet. Now, let's install and run those scripts
$ apk add alpine-conf
Let's set up our repository mirrors too
$ setup-apkrepos -f
-
Install base packages
Now we can install some base packages. While not all of them may be required in a WSL instance, it's better to be safe than sorry
$ apk update && apk upgrade $ apk add linux-lts linux-firmware-none acpi mkinitfs acpid alpine-sdk $ apk add busybox-openrc busybox-extras busybox-mdev-openrc openrc $ apk add llvm clang lld libc++ compiler-rt $ apk add nano build-base coreutils zip unzip git curl wget ca-certificates
-
Set root password
Don't forget to set a password for the root user:
$ passwd
-
Optional: Install zsh
As an optional step, you can install zsh if you prefer it over ash:
$ apk add zsh shadow $ chsh -s $(which zsh) # Now exit and re-enter AlpineWSL to see if it switched $ echo $SHELL /bin/zsh
-
Set up Privilege elevation
There are two main privilege elevation commands on Linux, and while
sudo
is the more common option, I'll be setting updoas
as it's the default in Alpine Linux and what's downloaded in when running thesetup-user
script -
Setup user
Setting up a user is super easy since we downloaded the setup scripts earlier:
$ setup-user -a Setup a user? (enter a lower-case loginname, or 'no') [no] your_chosen_username Full name for user your_chosen_username [your_chosen_username] your_chosen_full_name New password: Retype new password: passwd: password updated successfully Enter ssh key or URL for gmifflen (or 'none') [none] none (1/1) Installing doas (6.8.2-r7) Executing busybox-1.36.1-r29.trigger OK: 767 MiB in 92 packages # Let's check if our user was correctly created $ cat /etc/passwd root:x:0:0:root:/root:/bin/zsh bin:x:1:1:bin:/bin:/sbin/nologin ... your_chosen_username:x:1000:1000:your_chosen_username:/home/your_chosen_username:/bin/zsh # perfect, our user is now setup! # one last thing we should do is to link doas to sudo so that any scripts that use sudo will work without us having to install it $ ln -s $(which doas) /usr/bin/sudo
Now we just have to set our user as the default user in
/etc/wsl.conf
$ touch /etc/wsl.conf && nano /etc/wsl.conf # add this into the config [user] default = your_chosen_username
Once again, let's exit and re-enter our Alpine WSL instance, and we should be logged into our newly created user, but this time we have to fully terminate the WSL instance before re-entering
> wsl --terminate Alpine The operation completed successfully.
Now we should check if we have been logged into or user
% whoami your_chosen_username % pwd /home/your_chosen_username # lets check if our perms are set up correctly % doas apk update
Awesome, now we have our Alpine Linux WSL instance setup with a user and Privilege elevation. This is where I'll stop for the main part of the guide, I might add an extras section in the future for other QOL things we can set up to help with using WSL, but for now here's a rapid-fire list of useful things to look into on your own time:
doas apk add git
git config --system core.autocrlf false
: Disables Git’s automatic conversion between Windows-style line endings (\r\n
) and Unix-style line endings (\n
).git config --system core.symlinks false
: Disables Git’s handling of symbolic links.git config --system rebase.autosquash true
: Automatically squashes fixup commits during an interactive rebase.git config --system lfs.activitytimeout 0
: Sets the timeout for Git LFS (Large File Storage) operations to 0, meaning no timeout.git config --system credential.helper 'cache --timeout 28800'
: Configures Git to cache credentials (e.g., passwords or tokens) for 28,800 seconds (about 8 hours or a full day of work).git lfs install
: an extension to Git that helps manage large binary files by storing them outside the main Git repository and only referencing them- setting perms in
/etc/doas.d/doas.conf
topermit nopass :wheel
so you don't ever have to type your pass again (unsafe, but I don't personally care) - Updating to the latest wsl kernel https://github.com/microsoft/WSL2-Linux-Kernel
doas apk add wslu
: some useful utilities for using linux in wsl
Thanks to:
- lidgnulinux for the orginal gist
- yuk7 for wsldl
- sileshn for their
bash_profile
- dbilanoski for his article on Shell Folders
$shell = New-Object -ComObject Shell.Application
Folder
objects, allowing us to interact with them uniformly$shell.NameSpace("$PWD/icons.zip")
icons.zip
as a virtual folder inside the Shell Namespace, letting us treat it like a regular directory where its contents can be accessed without extraction.ParseName("Alpine.exe")
"Alpine.exe"
with the mounted zip folder, returning aFolderItem
object representing the file, and allowing us to do further operations on it$shell.NameSpace("$PWD")
Folder
object in the Shell Namespace, making it a valid target for file operations related to the already mountedicons.zip
.CopyHere(...)
Alpine.exe
from the mounter zip folder to our current working directory, extracting only the specified file from the virtual ZIP container to the physical file system