Categories
Snapcraft WSL

Systemd started in WSL2 when you login to Windows. You’ll be astounded by the speed improvement!

Update (2020/08/07): Since publishing this post I’ve added a bit more to the script used to enter the Systemd namespace. The latest version supports VSCode properly, with the bonus that the VSCode inbuilt shell is now correctly within the Systemd namespace.

Update (2020/08/20): Add fallback Systemd startup script for when you have run either wsl.exe --terminate $DISTRONAME or wsl.exe --shutdown, causing Systemd to be not running when starting the shell.


So, I wrote about and made a video about my hack to get Systemd running in WSL2. Now, I’ve managed to improve the performance of this hack by reducing the amount of indirection and moving the start-up of Systemd to be handled by Windows’ Task Scheduler.

First, we need an Ubuntu or other distro with daemonize installed. For the Ubuntu releases from the Windows Store you need to run the following:

sudo apt update sudo apt install -yyq daemonize
Code language: Bash (bash)

Next, we need a small script in /etc/profile.d to set up some variables when we login to WSL2:

export WSL_INTEROP="$(ls -t /run/WSL/*_interop | head -1)" export DISPLAY="$(awk '/namespace/ { print $2":0" }' /etc/resolv.conf)" if [ -f "$HOME/.wsl_env" ]; then set -a; source "$HOME/.wsl_env"; set +a fi
Code language: Bash (bash)

Moving on, we need a new file called /usr/bin/namespaced-shell-wrapper, which we symlink to /bin/namespaced-bash. This will then chain into /bin/bash. If you want to use an alternative shell such as /usr/bin/zsh you would symlink the wrapper to /usr/bin/namespaced-zsh. In this way it is anticipated that we can use any shell we desire.

#!/bin/sh ME="$0" SHELL="$(echo "$0" | sed -e 's/namespaced-//')" if [ "$1" = "-c" ]; then # this is for VSCode support shift SAVE_CMD="$@" if echo "$1" | grep -q '^sh -c'; then SAVE_CMD_NOSH="$(echo "$1" | sed -e 's/^sh -c//')" SAVE_CMD="$(eval echo "$SAVE_CMD_NOSH")" shift fi eval echo "$SAVE_CMD" > "$HOME/.wsl_cmd" fi if [ "$USER" != "root" ]; then export > "$HOME/.wsl_env" exec sudo "$ME" fi SYSTEMD_PID="$(ps -eo pid=,args= | awk '$2" "$3=="systemd --unit=multi-user.target" { print $1 }')" if [ -z "$SYSTEMD_PID" ]; then /usr/bin/daemonize /usr/bin/unshare --fork --mount-proc --pid -- /bin/sh -c "mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc; exec systemd --unit=multi-user.target" while [ -z "$SYSTEMD_PID" ]; do sleep 1 SYSTEMD_PID="$(ps -eo pid=,args= | awk '$2" "$3=="systemd --unit=multi-user.target" { print $1 }')" done sleep 1 fi CMD_FILE="$(eval echo "~$SUDO_USER/.wsl_cmd")" if [ -f "$CMD_FILE" ]; then trap "rm -f '$CMD_FILE'" EXIT nsenter -a -t "$SYSTEMD_PID" sudo -u "$SUDO_USER" "$SHELL" -c "$CMD" else exec nsenter -a -t "$SYSTEMD_PID" su --pty --shell="$SHELL" --login "$SUDO_USER" fi
Code language: Bash (bash)

We need another file at /etc/sudoers.d/namespaced-shells to allow password-less sudo for your chosen shells’ wrapper symlink to chain into itself as root. It should include something like below:

%sudo ALL=(ALL) NOPASSWD: /bin/namespaced-bash
Code language: plaintext (plaintext)

To set your shell you now need to execute the following:

sudo chsh -s /bin/namespaced-bash $USER
Code language: Bash (bash)

Finally, we need a Scheduled Task in Windows Task Scheduler to execute the following command on login to Windows. Make sure you remember to uncheck the “Start the task only if the computer is on AC power” option so that it starts Systemd if you’re on battery power when you login (this is in the “Conditions” tab of the task’s properties dialog):

wsl.exe -d Ubuntu -u root -e /bin/sh -c "/usr/bin/daemonize /usr/bin/unshare --fork --mount-proc --pid -- sh -c 'mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc; exec systemd --unit=multi-user.target'"
Code language: DOS .bat (dos)
Example of a scheduled task to start Systemd on login to Windows
Example of a scheduled task to start Systemd on login to Windows

If your chosen distribution is not Ubuntu, you might need to adjust the path to daemonize, unshare and/or systemd as appropriate. If you adjust the path to systemd then you also need to adjust it in the namespaced-shell-wrapper script to match. You need to set -d Ubuntu to your chosen WSL2 instance’s name for your system, e.g. -d openSUSE-Leap-15.2.

Systemd started a login final thought

This method speeds up entering WSL2 because we don’t need to go through a long chain of exec calls with various scripts and commands chaining into themselves to get Systemd running. Instead we start Systemd when we login to Windows so unless you terminate the distro or shutdown WSL2 it will always be ready.

Hat tip to Brian Ketelsen for their Tweet that got me thinking about this method.

Leave a Reply

Your email address will not be published. Required fields are marked *