From the content of previous posts, it’s no secret that I enjoy automating infrastructure to some degree. I have learned a few tools there, notably Terraform, because I value being able to recreate a instance from scratch with a simple setup and just a few commands. However, that does not exist for my personal machines. I’ve been exposed to NixOS via some introductory blog posts and understand the value therein, but have yet to bother with it. When I ran across Guix, my enjoyment lisp (yes, I know Guile is a Scheme) gave me the impetus to try.

Getting a Bootable USB

As a GNU project, Guix only uses free software by default. While it’d be great to use fully free software, I am not particularly a zealot, and I don’t want to replace my laptop. It has a certain network card

# lspci -k
...
02:00.0 Network controller: Intel Corporation Centrino Advance-N 6230 [Rainbow Peak] (rev 34)
        Subsystem: Intel Corporation Centrino Advance-N 6230
        Kernel driver in use: iwlwifi
        Kernel modules: iwlwifi

which means I need iwlwifi to connect to the internet. The reason this is a pain for Guix is that I cannot use their installer1. As far as I know, my next best option is to create my own machine image with the driver built in. That’s an eventual goal anyway, and Guix is very much designed to facilitate this. Luckily, a few examples for using non-free drivers, can be found around the web, so I have some basic guidance.

(operating-system
  (host-name "phone-microwave")
  (timezone "America/Chicago")
  (locale "en_US.utf8")

  (kernel linux-nonfree)
  (firmware (cons*
             iwlwifi-firmware-nonfree
             %base-firmware))
  ; ...
  )

You can look at the definitions of linux-nonfree and iwlwifi-firmware-nonfree in my source; I merely want to highlight how the operating-system is configured.

I knew I’d need this from past experience installing Linux on this machine, but I’m not terribly sure how well this would work on any other. If I had a Broadcom chip, how would I figure which driver to use? Remember all this has to be done in advance, because I create the machine image for target computer from a different one. And it can take quite awhile, so iteration is costly. Anyway, it’s nothing I need to worry about this time around.

An annoying part of installing an OS like Linux (okay, maybe just minimal ones like Arch) is having to configure your system

(operating-system
  ; ...
  (packages (cons* curl git man-db man-pages neovim nss-certs sudo %base-packages)))

If I have these, I can work in a mostly familiar manner when trying to troubleshoot on my way to a full setup. I’ll probably end up putting my window manager of choice here as well, but frankly I don’t feel a graphical environment is necessary for exploring what this distro is all about. In fact, none of my machines boot straight into a graphical environment. It’s hardly any extra effort to just type sway at the tty, and there are less things that can go wrong on startup.

Building

Alrighty, I need to set up my machine to build the image.

$ curl https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh -sO
$ chmod a+x guix-install.sh
$ sudo ./guix-install.sh
# ...
[ PASS ] Guix has successfully been installed!
[ INFO ] Run 'info guix' to read the manual.
$ time guix system build config.scm
# ...
real    118m40.393s
user    0m20.975s                                                                                                                      
sys     0m1.623s

Wow that took awhile; I just compiled the Linux kernel! ps aux tells me guix even had make using -j 8. This is certainly a lot more effort than downloading an iso like I’m used to with Arch. I’d imagine there’s a way to configure the kernel compilation process to only compile what I need for this machine. SO. MANY. DRIVERS. Something I hope to figure out down the road.

$ guix system disk-image config.scm
/gnu/store/fb4xwckszly2j04wjh5y910s4l31v686-disk-image
# time dd if=/gnu/store/fb4xwckszly2j04wjh5y910s4l31v686-disk-image of=/dev/sdb status=progress oflag=sync         
3933381120 bytes (3.9 GB, 3.7 GiB) copied, 14838 s, 265 kB/s                                                                           
7682451+0 records in                                                                                                                   
7682451+0 records out                                                                                                                  
3933414912 bytes (3.9 GB, 3.7 GiB) copied, 14838.8 s, 265 kB/s                                                                         
                                                                                                                                       
real    247m18.787s                                                                                                                    
user    1m6.996s                                                                                                                       
sys     11m44.969s                                                                                                                     

I will guard this USB stick with my life. After all, while you were out partying, I studied the command line.

Installation

After configuring the BIOS to boot from USB, I finally land in a tty.

login for phone-microwave: root
# which curl
/run/current-system/profile/bin/curl

There’s no root password initially, and it’s pretty neat to see the packages I want are already installed! But this is still just on the USB; I need to move it to my hard drive. And this is usually the part where you need working internet.

# wpa_passphrase "ssid" "password" > /etc/home-wifi.conf
# wpa_supplicant -i wlp2s0 -c /etc/home-wifi.conf -B
Successfully initialized wpa_supplicant
rfkill: WLAN soft blocked

Odd, I’ve never seen that before. A quick search clears it right up though:

# iwconfig wlp2s0 txpower auto
# wpa_supplicant -i wlp2s0 -c /etc/home-wifi.conf -B
Successfully initialized wpa_supplicant
# dhclient wlp2s0
...

Because this machine was already running Arch, the disk is provisioned in a sufficient way. I have one for the entire filesystem and another for swap.

# mkfs.ext4 /dev/sda1
# mount /dev/sda1 /mnt
# mkdir /mnt/etc
# cp /etc/configuration/desktop.scm /mnt/etc/config.scm
# time guix system init /mnt/etc/config.scm /mnt

It didn’t take too long for the program to run out of memory. I foolishly overlooked the warning about running:

# herd start cow-store /mnt

before the system init. Well, truth be told I tried to run it, but the cow-store service wasn’t in my image, so I ignored it and moved on.

Ugh back to square one. I build another machine image, this time forking the installer configuration rather than the normal OS.

(operating-system
  (inherit installation-os)
  ; ...
  )

After repeating the previous steps (and a lot more waiting),

gnu login: root (automatic login)

Welcome to the installation of GNU Guix

Using this shell, you can carry out the installation process "manually."
Access documentation at any time by pressing Alt-F2
root@gnu ~#

This is a little different, naturally because it’s an installation image. This time I can run the cow-store service and proceed with the same steps as above. After a few more hours, I finally have a working Guix system.

Configuration

Thanks to operating-system, I was most of the way there - I just want to try out a graphical environment. Open up /etc/config.scm and add a few entries:

  (packages (cons* font-adobe-source-code-pro
                   pulseaudio
                   qutebrowser
                   sway
                   swaylock
                   termite
                   upower
                   wl-clipboard
                   %base-packages))
# guix system reconfigure /etc/config.scm

Now I have a working desktop environment with sway! After cloning my dotfiles, I’m off to the races.

Thoughts so far

The package manager is SLOW. At this point, I believe it is recompiling a lot of programs from source, which one one hand is evidence that the package definitions are nice and repeatable, but it’s also a bit annoying to wait. And the output isn’t terribly friendly. I bet it’s providing the information I want, but I find the wording obtuse.

A few things such as my lock screen don’t work yet. I have a hunch it’s the difference between init systems - Guix uses Shepherd instead of systemd. Still got some work to do…

Special thanks to: