Sebastian's personal website

Running guix package manager on top of Fedora Silverblue

Written by Sebastian Dümcke on

I love the ideas behind guix, in particular the outlook to have a fully declarative system that can be reproduced on any new hardware. However, when I was playing around with guix in a virtual machine in the past, I managed to bork the system so that neither a reconfigure nor a roll-back was possible. Following the IRC channel over the past years I noticed that there are regularly issues brought up inhibiting upgrading a system or requiring manual intervention. To summarize: my impression is that guix as an operating system is lacking stability. However that should not deter you from using guix as a package manager, which already brings many great features and improvements over any other package manager, which I might present in a separate post

A few years back I have discovered Fedora Silverblue, an atomic, immutable operating system, which shares many attributes with guix. It also allows for roll-back and is somewhat declarative, in that a specific commit of the underlying OSTree defines the complete set of software installed on the system (modulo overlays). I have been using it for some time and really like it. So far every update went smoothly even when upgrading across 2 major versions. However, since Fedora Silverblue is immutable, meaning all mount points that do not contain user data are read-only, installing packages is burdensome. Luckily, I found that in combination with the guix package manager we get the best of both worlds: a rock solid base system and the ability to install any packages in any version at the same time.

Due to the immutable nature of Silverblue and its focus on FHS compatibilty, a few steps are necessary to install guix. We will install the store to /var/gnustore (/var is mutable in Silverblue) and bind mount it to /gnu/store to ensure we can use substitutes.

First we need to set SELinux to permissive mode. While there is documentation for SE Linux support, I could not manage to make it run under SELinux. So open vi /etc/selinux/config and set SELINUX=permissive

Then download the latest binary release from guix and perform the following steps (mirroring what the install script does):

#unpack the guix archive on host
GUIX_ARCHIVE=5i530v4c67iyq49qji2v2wxdgf5cdq5l-guix-binary.tar.xz
tar xvf $GUIX_ARCHIVE 
#mv gnu/store to /var/gnustore and guix to /var/guix
sudo mv gnu/store /var/gnustore
sudo mv var/guix /var/

Add the group and users for the guix daemon

#configure groups and users on host
sudo groupadd --system guixbuild
for i in $(seq -w 1 10);
do
    sudo useradd -g guixbuild -G guixbuild \
                -d /var/empty -s /usr/sbin/nologin \
                -c "Guix build user $i" --system \
                guixbuilder$i;
done

Add two systemD mount services to create the bind mount /var/gnustore -> /gnu/store for guix. The file names are indicated in the comment.

#/etc/systemd/system/mkdir-rootfs@.service
[Unit]
Description=Enable mount points in / for ostree
DefaultDependencies=no
ConditionPathExists=!%f

[Service]
Type=oneshot
ExecStartPre=chattr -i /
ExecStart=mkdir -p '%f'
ExecStopPost=chattr +i /
#/etc/systemd/system/gnu-store.mount
[Unit]
Description=Read-only /gnu/store for GNU Guix adapted to Silverblue
After=mkdir-rootfs@gnu-store.service
Wants=mkdir-rootfs@gnu-store.service
Before=guix-daemon.service

[Install]
WantedBy=guix-daemon.service

[Mount]
What=/var/gnustore
Where=/gnu/store
Type=none
Options=bind,ro

We also copy over the guix-daemon systemD service from the guix tarball to our system.

sudo cp /var/guix/profiles/per-user/root/current-guix/lib/systemd/system/guix-daemon.service /etc/systemd/system

Now we override this service definition to ensure it runs after the bind mount was created by running sudo systemctl edit guix-daemon and adding the following:

[Unit]
Wants=gnu-store.mount
After=gnu-store.mount

I had some issues with the guix-daemon being loaded before the bind mount without these overrides. Now we can enable and start the guix daemon service: sudo systemctl --now enable guix-daemon

Activate substitute servers then run guix pull, both using the root version of guix. This should then install a local guix.

sudo /var/guix/profiles/per-user/root/current-guix/bin/guix archive --authorize < /var/guix/profiles/per-user/root/current-guix/share/guix/ci.guix.gnu.org.pub
sudo /var/guix/profiles/per-user/root/current-guix/bin/guix archive --authorize < /var/guix/profiles/per-user/root/current-guix/share/guix/bordeaux.guix.gnu.org.pub
/var/guix/profiles/per-user/root/current-guix/bin/guix pull

After setting and sourcing the GUIX_PROFILE variable to our local guix we have a running guix on our system:

GUIX_PROFILE="/var/home/user/.config/guix/current"
 . "$GUIX_PROFILE/etc/profile"

Optional: add the shell completions

#install shell completion
for i in /var/guix/profiles/per-user/root/current-guix/etc/bash_completion.d/*
do
  sudo ln -sf $i /etc/bash_completion.d/$(basename $i)
done