{"id":15197127,"url":"https://github.com/andyrids/alpine-wsl-dev","last_synced_at":"2025-10-28T07:31:14.131Z","repository":{"id":253290870,"uuid":"842997583","full_name":"andyrids/alpine-wsl-dev","owner":"andyrids","description":"Alpine Linux on WSL2 dev environment setup","archived":false,"fork":false,"pushed_at":"2024-08-15T17:47:57.000Z","size":144,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-01T12:44:14.243Z","etag":null,"topics":["alpinelinux","hotplug","mdev","powershell-script","raspberrypi","usbip-win","wsl"],"latest_commit_sha":null,"homepage":"","language":"PowerShell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andyrids.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-15T15:00:10.000Z","updated_at":"2025-01-19T16:18:50.000Z","dependencies_parsed_at":"2024-08-15T19:52:48.686Z","dependency_job_id":"66b22c09-e1f5-48bd-a10e-cc24603f1e2e","html_url":"https://github.com/andyrids/alpine-wsl-dev","commit_stats":null,"previous_names":["andyrids/alpine-wsl-dev"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyrids%2Falpine-wsl-dev","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyrids%2Falpine-wsl-dev/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyrids%2Falpine-wsl-dev/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyrids%2Falpine-wsl-dev/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andyrids","download_url":"https://codeload.github.com/andyrids/alpine-wsl-dev/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238614401,"owners_count":19501451,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alpinelinux","hotplug","mdev","powershell-script","raspberrypi","usbip-win","wsl"],"created_at":"2024-09-28T00:42:58.557Z","updated_at":"2025-10-28T07:31:08.820Z","avatar_url":"https://github.com/andyrids.png","language":"PowerShell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Alpine-WSL-Dev\n\nThis repository contains instructions and scripts used to install and setup an Alpine Linux minimal root filesystem on WSL2.\n\n[Alpine Linux](https://alpinelinux.org/about/) is an independent, non-commercial, general purpose Linux distribution. Alpine Linux is built around musl libc and BusyBox. This makes it small and very resource efficient. A container requires no more than 8 MB and a minimal installation to disk requires around 130 MB of storage.\n\n## Introduction\n\nThis setup is used mainly for Python development on WSL, but is also used for front-end projects and embedded projects using MicroPython and C on Raspberry Pi devices, which must be attached to WSL using [usbipd-win](https://github.com/dorssel/usbipd-win).\n\nOpenRC is the init system used in alpine. The init system manages the services, such as mdev, which is the default device manager on Alpine Linux. It is provided by busybox as alternative to systemd's udev. This setup allows mdev to act as a hotplug manager and facilitates access to USB and COM devices when attached to WSL distributions, by dynamically updating `/dev` based on your mdev config rules.\n\nI prefer to use [Windows Terminal](https://apps.microsoft.com/detail/9n0dx20hk701?hl=en-US\u0026gl=US) to manage my profiles for PowerShell and WSL distributions.\n\n\u003c!-- [!TIP] [!IMPORTANT] [!WARNING] [!CAUTION] --\u003e\n\n\u003e [!NOTE]\n\u003e mdev is not recommended for a full blown desktop environment, but it is perfect for a minimal WSL install.\n\n\n## 1. Download \u0026 install Alpine Linux\n\nThe minimal root filesystem can be downloaded from [Alpine Linux Downloads](https://alpinelinux.org/downloads/). I create a WSL directory and a subdirectory for each distribution (e.g. `C:\\WSL\\Alpine`), where I keep my distribution images and where I have WSL create the vhdx image files. \n\nOn an administrator PowerShell, Alpine can be installed using the following commands:\n\n```ps\nwsl --import Alpine C:\\WSL\\Alpine C:\\WSL\\Alpine\\alpine-minirootfs-3.20.1-x86_64.tar\n```\nCheck installation by listing distributions with verbose output:\n\n```ps\nwsl -l -v\n```\n\n## 2. Setup Alpine Linux\n\nI use a profile within Windows Terminal (Microsoft Store), which is set to run Alpine with the following command `C:\\windows\\system32\\wsl.exe -d Alpine`. See here [Alpine Linux post-install guide](https://docs.alpinelinux.org/user-handbook/0.1a/Working/post-install.html) for a useful post-install guide.\n\n### New user setup\n\nNew user setup - replace \u003cusername\u003e with your username:\n\n```sh\nadduser -h /home/username -s /bin/ash username\nsu -l root\n```\n\nThis setup uses doas rather than sudo and is more minimal:\n\n```sh\napk update \u0026\u0026 apk upgrade\napk add doas\n```\n\nAdd the new user to the wheel group:\n\n```sh\necho 'permit :wheel' \u003e /etc/doas.d/doas.conf\nadduser username wheel\n```\n \nMicrocontrollers attached to WSL are often placed in the dialout group by mdev rules (/dev/ttyACM[0-9]). The command below\nadds the user to the dialout group to facilitate connecting to these devices (if needed):\n\n```sh\nadduser username dialout\n```\n\nIn Alpine, the doas.conf file is at the path `/etc/doas.d/`:\n\n```sh\nsu -l username\ndoas vi /etc/doas.d/doas.conf\n```\n\nBelow is an example for `/etc/doas.d/doas.conf` settings, which allow a user to perform\napk update and apk upgrade as root without a password:\n\n```sh\n# permit apk update \u0026 apk upgrade without password for user \u003cusername\u003e\npermit nopass username as root cmd apk args update\npermit nopass username as root cmd apk args upgrade\n```\n\nTest doas config settings:\n\n```sh\ndoas apk update \u0026\u0026 doas apk upgrade\n```\n\n### OpenRC, mdev \u0026 hwdrivers setup\n\nBelow details installation \u0026 setup of OpenRC with mdev and hwdrivers on WSL2. mdev and OpenRC can be installed using apk:\n\n```sh\ndoas apk add busybox-mdev-openrc\n```\n\nEnable mdev \u0026 hwdrivers services at the sysinit runlevel:\n\n```sh\ndoas rc-update add mdev sysinit\ndoas rc-update add hwdrivers sysinit\n```\nCheck sysinit runlevel services:\n\n```sh\nrc-status sysinit\n```\n\nStart mdev \u0026 hwdrivers, if currently stopped:\n\n```sh\ndoas rc-service mdev --ifstopped start\ndoas rc-service hwdrivers --ifstopped start\n```\n\nManually seed `/dev` with device nodes based on current mdev config file:\n\n```sh\ndoas mdev -s\n```\nView changes:\n\n```sh\nls -la /dev\n```\n\nBelow is my init file for mdev at `/etc/init.d/mdev`, which I modified to run mdev as a daemon, which allowed me to\nhave mdev run as a hotplug manager similar to udev. Whenever I plug in a Raspberry Pi device and share and attach it \nto WSL, I want mdev to respond to a uevent and parse the `/etc/mdev.conf` looking for matching rules for the attached \ndevice, which is usually ttyACM[0-9]. The modified line is `mdev -d` in the `_start_service` function, which replaced\n`mdev -s`.\n\n```sh\n#!/sbin/openrc-run\n\ndescription=\"the mdev device manager\"\n\ndepend() {\n        provide dev\n        need sysfs dev-mount\n        before checkfs fsck\n        keyword -containers -vserver -lxc\n}\n\n_start_service () {\n        ebegin \"Starting busybox mdev\"\n        mkdir -p /dev\n        echo \u003e/dev/mdev.seq\n        echo \"/sbin/mdev\" \u003e /proc/sys/kernel/hotplug\n        eend $?\n}\n\n_start_coldplug () {\n        ebegin \"Scanning hardware for mdev\"\n        # mdev -s will not create /dev/usb[1-9] devices with recent kernels\n        # so we manually trigger events for usb\n        for i in $(find /sys/devices -name 'usb[0-9]*'); do\n                [ -e $i/uevent ] \u0026\u0026 echo add \u003e $i/uevent\n        done\n        # trigger the rest of the coldplug\n        # mdev -s is replaced with mdev -d to have it run as a daemon\n        mdev -d\n        eend $?\n}\n\nstart() {\n        _start_service\n        _start_coldplug\n}\n\nstop() {\n        ebegin \"Stopping busybox mdev\"\n        echo \u003e /proc/sys/kernel/hotplug\n        eend\n}\n```\n\nBelow is the mdev rule in the `/etc/mdev.conf` file which is relevant to RPi Microcontrollers and probably Arduino devices:\n\n```sh\nttyACM[0-9]     root:dialout 0660 @ln -sf $MDEV modem\n```\n\nHere is the full `/etc/mdev.conf` file and corresponding rules:\n\n```sh\n#\n# This is a sample mdev.conf.\n#\n\n# Devices:\n# Syntax: %s %d:%d %s\n# devices user:group mode\n\n$MODALIAS=.*    root:root       0660    @modprobe -q -b \"$MODALIAS\"\n\n# null does already exist; therefore ownership has to be changed with command\nnull    root:root 0666  @chmod 666 $MDEV\nzero    root:root 0666\nfull    root:root 0666\n\nrandom  root:root 0666\nurandom root:root 0444\nhwrandom root:root 0660\n\nconsole root:tty 0600\n\n# load frambuffer console when first frambuffer is found\nfb0     root:video 0660 @modprobe -q -b fbcon\n\nfd0     root:floppy 0660\nkmem    root:kmem 0640\nmem     root:kmem 0640\nport    root:kmem 0640\nptmx    root:tty 0666\n\n# Kernel-based Virtual Machine.\nkvm             root:kvm 660\n\n# ram.*\nram([0-9]*)     root:disk 0660 \u003erd/%1\nloop([0-9]+)    root:disk 0660 \u003eloop/%1\n\n# persistent storage\ndasd.*          root:disk 0660 */lib/mdev/persistent-storage\nmmcblk.*        root:disk 0660 */lib/mdev/persistent-storage\nnbd.*           root:disk 0660 */lib/mdev/persistent-storage\nnvme.*          root:disk 0660 */lib/mdev/persistent-storage\nsd[a-z].*       root:disk 0660 */lib/mdev/persistent-storage\nsr[0-9]+        root:cdrom 0660 */lib/mdev/persistent-storage\nvd[a-z].*       root:disk 0660 */lib/mdev/persistent-storage\nxvd[a-z].*      root:disk 0660 */lib/mdev/persistent-storage\n\nmd[0-9]         root:disk 0660\n\ntty             root:tty 0666\ntty[0-9]        root:root 0600\ntty[0-9][0-9]   root:tty 0660\nttyS[0-9]*      root:dialout 0660\nttyGS[0-9]      root:root 0660\npty.*           root:tty 0660\nvcs[0-9]*       root:tty 0660\nvcsa[0-9]*      root:tty 0660\n\n# rpi bluetooth\n#ttyAMA0        root:tty 660 @btattach -B /dev/$MDEV -P bcm -S 115200 -N \u0026\n\nttyACM[0-9]     root:dialout 0660 @ln -sf $MDEV modem\nttyUSB[0-9]     root:dialout 0660 @ln -sf $MDEV modem\nttyLTM[0-9]     root:dialout 0660 @ln -sf $MDEV modem\nttySHSF[0-9]    root:dialout 0660 @ln -sf $MDEV modem\nslamr           root:dialout 0660 @ln -sf $MDEV slamr0\nslusb           root:dialout 0660 @ln -sf $MDEV slusb0\nfuse            root:root  0666\n\n# mobile broadband modems\ncdc-wdm[0-9]+   root:dialout 0660\n\n# dri device\ndri/.*          root:video 0660\ncard[0-9]       root:video 0660 =dri/\n\n# alsa sound devices and audio stuff\npcm.*           root:audio 0660 =snd/\ncontrol.*       root:audio 0660 =snd/\nmidi.*          root:audio 0660 =snd/\nseq             root:audio 0660 =snd/\ntimer           root:audio 0660 =snd/\n\nadsp            root:audio 0660 \u003esound/\naudio           root:audio 0660 \u003esound/\ndsp             root:audio 0660 \u003esound/\nmixer           root:audio 0660 \u003esound/\nsequencer.*     root:audio 0660 \u003esound/\n\nSUBSYSTEM=sound;.*      root:audio 0660\n\n# PTP devices\nptp[0-9]        root:root 0660 */lib/mdev/ptpdev\n\n# virtio-ports\nSUBSYSTEM=virtio-ports;vport.* root:root 0600 @mkdir -p virtio-ports; ln -sf ../$MDEV virtio-ports/$(cat /sys/class/virtio-ports/$MDEV/name)\n\n# misc stuff\nagpgart         root:root 0660  \u003emisc/\npsaux           root:root 0660  \u003emisc/\nrtc             root:root 0664  \u003emisc/\n\n# input stuff\nSUBSYSTEM=input;.*  root:input 0660\n\n# v4l stuff\nvbi[0-9]        root:video 0660 \u003ev4l/\nvideo[0-9]+     root:video 0660 \u003ev4l/\n\n# dvb stuff\ndvb.*           root:video 0660 */lib/mdev/dvbdev\n\n# VideoCore VC4 BCM GPU specific (as in Pi devices)\nvchiq   root:video 0660\nvcio    root:video 0660\nvcsm-cma        root:video 0660\nvc-mem  root:video 0660\n\n# load drivers for usb devices\nusb[0-9]+       root:root 0660 */lib/mdev/usbdev\n\n# net devices\n# 666 is fine: https://www.kernel.org/doc/Documentation/networking/tuntap.txt\nnet/tun[0-9]*   root:netdev 0666\nnet/tap[0-9]*   root:netdev 0666\n\n# zaptel devices\nzap(.*)         root:dialout 0660 =zap/%1\ndahdi!(.*)      root:dialout 0660 =dahdi/%1\ndahdi/(.*)      root:dialout 0660 =dahdi/%1\n\n# raid controllers\ncciss!(.*)      root:disk 0660 =cciss/%1\ncciss/(.*)      root:disk 0660 =cciss/%1\nida!(.*)        root:disk 0660 =ida/%1\nida/(.*)        root:disk 0660 =ida/%1\nrd!(.*)         root:disk 0660 =rd/%1\nrd/(.*)         root:disk 0660 =rd/%1\n\n# tape devices\nnst[0-9]+.*     root:tape 0660\nst[0-9]+.*      root:tape 0660\n\n# VirtualBox devices\nvboxguest   root:root 0600\nvboxuser    root:root 0666\nvboxdrv     root:root 0600\nvboxdrvu    root:root 0666\nvboxnetctl  root:root 0600\n\n# fallback for any!device -\u003e any/device\n(.*)!(.*)       root:root 0660 =%1/%2\n```\n\n### WSL configuration file\n\nI configure WSL with the following config file at `/etc/wsl.conf`. I set the default WSL username under the `[user]` \nsettings and facilitate openrc starting with WSL, by using the command `command = \"/sbin/openrc default\"` under `[boot]`. \nThe `appendWindowsPath = true` setting under `[interop]`, allows the addition of Windows tools to be added to the WSL distro \n`$PATH` automatically. The command `code .` would for example, be available in WSL and launch VSCode in the current working WSL \ndirectory.\n\n```sh\n# /etc/wsl.conf\n[automount]\nenabled = true\nmountFsTab = true\n\n[network]\ngenerateHosts = true\ngenerateResolvConf = true\n\n[interop]\nenabled = true\nappendWindowsPath = true\n\n[user]\ndefault = username\n\n[boot]\ncommand = \"/sbin/openrc default\"\n```\n\n### Profile configuration\n\nWhen we imported this distro of Alpine Linux manually, the `/etc/profile` file overwrites the `$PATH` environment variable and therefore \noverwrites the directories added by Windows from the aforementioned `appendWindowsPath = true` setting. I correct this by editing the \n`/etc/profile` file. You could also add anything else to the path here such as `export PATH=\"$PATH:/home/username/projects/.venv-poetry/bin\"`,\nwhich in my case, would add my Poetry package manager to the PATH.\n\n\n```sh\n# replace the existing PATH declaration:\nexport PATH=\"$PATH\"\n\nexport PAGER=less\numask 022\n\n# use nicer PS1 for bash and busybox ash\nif [ -n \"$BASH_VERSION\" -o \"$BB_ASH_VERSION\" ]; then\n        PS1='\\h:\\w\\$ '\n# use nicer PS1 for zsh\nelif [ -n \"$ZSH_VERSION\" ]; then\n        PS1='%m:%~%# '\n# set up fallback default PS1\nelse\n        : \"${HOSTNAME:=$(hostname)}\"\n        PS1='${HOSTNAME%%.*}:$PWD'\n        [ \"$(id -u)\" -eq 0 ] \u0026\u0026 PS1=\"${PS1}# \" || PS1=\"${PS1}\\$ \"\nfi\n\nfor script in /etc/profile.d/*.sh ; do\n        if [ -r \"$script\" ] ; then\n                . \"$script\"\n        fi\ndone\nunset script\n```\n### Restart WSL \n\nI reboot and restart Alpine linux to let the new configs take effect.\n\nWith the below command, you should see mdev \u0026 hwdrivers under syinit runlevel:\n\n```sh\nrc-update show -v\n```\n\nCheck mdev and hwdrivers services are started:\n\n```sh\n# should see * status: started for both services\nrc-service mdev status \u0026\u0026 rc-service hwdrivers status\n```\n\n## 3. Setup USB Device Sharing to WSL2 (Microcontrollers)\n\nI use usbipd-win to share locally connected USB devices to other machines, including Hyper-V guests and WSL 2.\nBy default devices are not shared with USBIP clients. To lookup and share devices, run the following commands with \nadministrator privileges:\n\nInstall usbipd-win with winget:\n```ps1\nwinget install usbipd\n```\n\nList devices with busid \u0026 state information using `usbipd list` command:\n\n```ps1\nusbipd list\n```\n\nThe below image shows a connected Raspberry Pi Pico W device (highlighted in blue), with MicroPython installed. Raspberry Pi Vendor ID (VID) is '0x2e8a' and the\nProduct ID (PID) for MicroPython is '0x0005'. The device has been 'Shared' using the command `usbipd bind --busid 1-7`. All devices must be shared before they can be attached.\n\n![usbipd list example](images/usbipd-list.png)\n\nTo share your device run the following command with the relevant busid value seen in the `usbipd list` output:\n\n```ps1\nusbipd bind --busid 1-7\n```\n![usbipd list example](images/usbipd-bind.png)\n\nAs long as WSL is running, you can then attach the device to WSL using the following command:\n\n```ps1\nusbipd attach --wsl --busid 1-7\n```\n![usbipd list example](images/usbipd-attach.png)\n\n\nIn Alpine Linux you can test the device is attached using the `lsusb` command and by checking that the mdev hotplug settings have been\nutilised with the following command:\n\n```sh\nls -la /dev/ttyACM*\n```\nThe below image shows the command result before and after attachment:\n\n![usbipd list example](images/alpine-ls-dev.png)\n\n## 4. Powershell Script to Automate Device Sharing to WSL2\n\nThe `Watch-PicoConnect.ps1` script can be used to automate Pico attachment to WSL on connection. I run this script in an Administrator PowerShell instance using the following command:\n\n```ps1\n. .\\Watch-PicoConnect.ps1\n```\n![usbipd list example](images/pico-connect-script.png)\n\n\u003e [!WARNING]\n\u003e This script is still in development and might need tweaking for your requirements.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandyrids%2Falpine-wsl-dev","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandyrids%2Falpine-wsl-dev","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandyrids%2Falpine-wsl-dev/lists"}