System Crafters

Installing GuixSD on WSL2

The Windows Subsystem for Linux enables Windows to run software, including whole distributions, written for Linux. This article goes through the process of installing GuixSD as a WSL distribution.

Note: GuixSD refers to the Guix System Distribution. Not Guix the package manager. This article won't cover how to install the package manager into an existing distribution (like Ubuntu or Debian), but rather how to create a custom distribution containing the full Guix system.

Table Of Contents

Prerequisites

Base Distribution

Guix will use a minimal WSL distribution based on busybox by 0xbadfca11. Download the rootfs.tgz of the latest release here. However, one could also create a rootfs from scratch. In any case, with an appropriate rootfs.tgz available, open up PowerShell, navigate to the folder with the file and execute

wsl --import guix /guix rootfs.tgz --version 2

Note: The --version 2 flag specifies the WSL version to use and must not be changed.

Roll back

The current distribution can erased by executing

wsl --unregister guix

Windows will delete any files associated with the given distribution. One may then start from scratch again.

Installation

The following script adds the folder and files required by Guix so that a full system can be built.

#!/bin/sh

# Creating necessary folders
mkdir -p /root /etc/guix /tmp /var/run /run /home
chmod 1777 /tmp

# Adding guix workers
rm /etc/passwd
cat <<EOM >> /etc/passwd
root:x:0:0:root:/root:/bin/bash
guixbuilder01:x:999:999:Guix build user 01:/var/empty:/usr/sbin/nologin
guixbuilder02:x:998:999:Guix build user 02:/var/empty:/usr/sbin/nologin
guixbuilder03:x:997:999:Guix build user 03:/var/empty:/usr/sbin/nologin
guixbuilder04:x:996:999:Guix build user 04:/var/empty:/usr/sbin/nologin
guixbuilder05:x:995:999:Guix build user 05:/var/empty:/usr/sbin/nologin
guixbuilder06:x:994:999:Guix build user 06:/var/empty:/usr/sbin/nologin
guixbuilder07:x:993:999:Guix build user 07:/var/empty:/usr/sbin/nologin
guixbuilder08:x:992:999:Guix build user 08:/var/empty:/usr/sbin/nologin
guixbuilder09:x:991:999:Guix build user 09:/var/empty:/usr/sbin/nologin
guixbuilder10:x:990:999:Guix build user 10:/var/empty:/usr/sbin/nologin
EOM

rm /etc/group
cat <<EOM >> /etc/group
root:x:0:
guixbuild:x:999:guixbuilder01,guixbuilder02,guixbuilder03,guixbuilder04,guixbuilder05,guixbuilder06,guixbuilder07,guixbuilder08,guixbuilder09,guixbuilder10
EOM

# Adding services
cat <<EOM >> /etc/services
ftp-data        20/tcp
ftp             21/tcp
ssh             22/tcp                          # SSH Remote Login Protocol
domain          53/tcp                          # Domain Name Server
domain          53/udp
http            80/tcp          www             # WorldWideWeb HTTP
https           443/tcp                         # http protocol over TLS/SSL
ftps-data       989/tcp                         # FTP over SSL (data)
ftps            990/tcp
http-alt        8080/tcp        webcache        # WWW caching service
http-alt        8080/udp
EOM

# Adding Guix channels
cat <<EOM >> /etc/guix/channels.scm
    ;; Your guix channels here
EOM

# Preparing environment
cd /tmp
wget http://ftp.gnu.org/gnu/guix/guix-binary-1.3.0.x86_64-linux.tar.xz
tar -C / -xvJf /tmp/guix-binary-1.3.0.x86_64-linux.tar.xz
mkdir -p ~root/.config/guix
ln -sf /var/guix/profiles/per-user/root/current-guix ~root/.config/guix/current
GUIX_PROFILE="`echo ~root`/.config/guix/current"
source $GUIX_PROFILE/etc/profile
guix-daemon --build-users-group=guixbuild &
guix archive --authorize < /var/guix/profiles/per-user/root/current-guix/share/guix/ci.guix.gnu.org.pub

# Reconfiguring the system
guix system reconfigure --no-bootloader --no-grafts -L $(dirname $(readlink -f $1)) $1

Custom Guix channels can be added here

# Adding Guix channels
cat <<EOM >> /etc/guix/channels.scm
    ;; Your guix channels here
EOM

If this is not required, the lines can be safely deleted. In any case, this script should copied to a location accessible by both Windows and the WSL distribution (E.g. C:\Users\<user>\Desktop\guix\guix-install.sh).

Manifest

Guix needs a manifest file as a blueprint to build the system. This minimal scheme file contains everything needed for a successful installation:

(define-module (wsl)
  #:use-module (gnu)
  #:use-module (gnu services ssh)
  #:use-module (gnu services networking)
  #:use-module (gnu packages version-control)
  #:use-module (guix channels)
  #:use-module (guix packages)
  #:use-module (guix profiles)
  #:use-module (ice-9 pretty-print)
  #:use-module (srfi srfi-1))

(define-public wsl-operating-system
  (operating-system
   (host-name "guix")
   (keyboard-layout (keyboard-layout "us" "altgr-intl"))
   (timezone "Europe/Paris")

   ;; User account
   (users (cons (user-account
                 (name "wsl")
                 (group "users")
                 (home-directory "/home/wsl")
                 (supplementary-groups '("wheel")))
                %base-user-accounts))

   (kernel hello)
   (initrd (lambda* (. rest) (plain-file "dummyinitrd" "dummyinitrd")))
   (initrd-modules '())
   (firmware '())

   (bootloader
    (bootloader-configuration
     (bootloader
      (bootloader
       (name 'dummybootloader)
       (package hello)
       (configuration-file "/dev/null")
       (configuration-file-generator (lambda* (. rest) (computed-file "dummybootloader" #~(mkdir #$output))))
       (installer #~(const #t))))))

   (file-systems (list (file-system
                        (device "/dev/sdb")
                        (mount-point "/")
                        (type "ext4")
                        (mount? #t))))

   (services (list (service guix-service-type)
                   (service special-files-service-type
                            `(("/usr/bin/env" ,(file-append coreutils "/bin/env"))))))))
wsl-operating-system

Place the file in the same folder as the script above. Inside PowerShell, execute

wsl -d guix --exec /bin/busybox sh -c "/mnt/c/path/to/guix-install.sh /mnt/c/path/to/wsl.scm"

The path is relative to the root folder of the WSL distribution. If the two files are located at C:\Users\<user>\Desktop\guix the path would then be /mnt/c/Users/<user>/Desktop/guix.

Note: The install script and the manifest file don't have to be in the same folder. The script also sets the load path to the folder containing the manifest file, this means wsl.scm may inherit from other modules located in the same load path (like a base-system.scm for example).

Initialization

After the installation is finished, it will most likely output a warning along the lines of

guix system: warning: while talking to shepherd: No such file or directory

This is to be expected. Because WSL distributions don't boot the same way a normal distribution would, Guix could not populate /run. More information about this can be found here. This has to be done manually or rather automated via a shell script:

#!/bin/busybox sh

export GUIX_NEW_SYSTEM=$(/bin/busybox readlink -f /var/guix/profiles/system)
# $GUIX_NEW_SYSTEM/boot needs this to exist even though /run is expected to be empty.
# I installed GuixSD in a proper VM and /run is not on tmpfs, so I'm not sure.
/bin/busybox ln -s none /run/current-system
setsid /var/guix/profiles/system/profile/bin/guile --no-auto-compile $GUIX_NEW_SYSTEM/boot >/dev/null &

/bin/busybox sleep 3
source /etc/profile

# why are these permissions not there in the first place?
for f in ping su sudo; do
        chmod 4755 $(readlink -f $(which $f))
done

# Setting up WSLg
if [ -d "/mnt/wslg" ]; then
        rm -r /tmp/.X11-unix
        ln -s /mnt/wslg/.X11-unix /tmp/.X11-unix
        # Add "export DISPLAY=:0" in your .bashrc!
fi

su -l <user>

Change the last line to your user name and copy the script to the same folder like the other scripts and execute it with

wsl -d guix --exec /bin/busybox sh -c "/mnt/c/path/to/guix-init.sh"

This script can be safely run every time one logs into the distribution (See Tidying up), however, it needs to be run after rebooting the host system or executing wsl -t guix or wsl --shutdown. The script will automatically log your user in after setting up the system.

A bash prompt should be waiting for input. Congratulations.

Now is a good time to set some passwords

passwd
passwd <user>

One can either switch to the user with su -l <user> or log the user in directly by executing wsl -u <user> -d guix.

Updating the system

The system can be updated like one would expect:

guix pull
sudo guix system reconfigure /mnt/c/path/to/wsl.scm

This creates a new system generation and switches to it.

Tidying up

At this point the installation is complete. However there are a couple of things that can be done to make interacting with the distribution easier. At the moment, there are three files on the host file system. The installation script (guix-install.sh) can be deleted as it is not needed anymore.

wsl.scm is only really needed inside the distribution. One can save it in the user space, for example

mkdir -p $HOME/.config/guix/manifests && mv /mnt/c/Users/<user>/Desktop/guix/wsl.scm $HOME/.config/guix/manifests

moves the file to ~/.config/guix/manifests.

guix-init.sh can be copied to /root/boot.sh and the distribution started by executing

wsl -d guix --exec /bin/busybox sh -c "/root/boot.sh"

This is really up to the individual setup, both files may very well be incorporated into an already existing configuration. minikN's dotfiles showcase a possible approach to this.

GUI applications

Launching GUI applications from within WSL assumes a working X server running on the host. There a couple of alternatives to consider:

Note: Both Xming and VcXsrv may suffer from display glitches when using Emacs' child frames due to an error in their GLX implementation.

This guide will not focus on how to configure each X server, because there are already plenty of resources available on the subject.

Once The X server is up and running, the DISPLAY variable has to be populated properly. A wrapper script can be used for this purpose (although, as always, there are other ways to achieve the same thing):

if uname -r | grep -q 'microsoft'; then
    export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
    export LIBGL_ALWAYS_INDIRECT=1
    export XCURSOR_SIZE=16
    setsid $1
fi

Note: If WSLg is the preferred option to run GUI applications, the init script automatically sets up the necessary X socket. One only needs to

export DISPLAY=:0

somewhere in user space (.profile, .bash_profile, ...). Do not use the provided wrapper script for this approach as it will set $DISPLAY wrong.

This script has to be available somewhere in the PATH. It should also be named appropriately and made executable: chmod +x run-wsl.

GUI applications can now be started with

run-wsl emacs

from within the distribution itself. However, it's more convenient to launch them from Windows directly via desktop shortcuts. In order to do that a minimal generic launcher can be written in vbs like so:

WScript.CreateObject("WScript.Shell").Run "wsl ~ -u <user> -d guix /path/to/run-wsl " & WScript.Arguments(0), 0, false

Note: Adjust the <user> and the path to the script accordingly.

Desktop shortcuts

This launcher will run the run-wsl script with its first argument. Now shortcuts for applications can be added by creating a shortcut to the launcher itself (Right click -> Send to -> Desktop (create shortcut)). After that edit the shortcuts target like so: C:\Users\<user>\Desktop\guix-launcher.vbs emacs where emacs is the application to launch.

The launcher can obviously reside anywhere on the file system, doesn't have to be the desktop. One may also change the shortcuts icon to something more appropriate like the emacs icon.

Sources