Click here for the HTML version
I have recently decided to move my desktop to an immutable system, more specifically openSUSE’s MicroOS.
Since MicroOS doesn’t use a traditional package manager like apt
or zypper
, it’s necessary to use containerised or otherwise distribution-independent solutions for installing packages.
The usual approach is using flatpak
for GUI applications, and distrobox
to create containers for CLI utilities and wherever else it is needed.
I personally have multiple issues with using containers:
- They aren’t transactionally updated - my containerized system can still break and cause trouble, even if limited just to the container this is still troublesome in my opinion.
- The tooling is somewhat lacking in my opinion - for example exporting binaries from a container is a little awkward.
The solution? Guix.
Guix allows for easy installations of software on MicroOS - there is little difference in usage compared to, say, apt
.
However, installing Guix on MicroOS has proven itself a difficult task - so I’ve decided to outline the steps for anyone looking to do the same.
- Unfortunately it is currently not possible to use Guix with SELinux - for that reason SELinux will be set to
permissive
mode. This has certain security implications - I’m not a security expert so you are free to do your own research for what these are.- I’ll say that from what I’ve seen the general consensus seems that using
permissive
is acceptable on a desktop.
- I’ll say that from what I’ve seen the general consensus seems that using
- Graphical programs installed with Guix won’t appear in the system menus until a re-login.
- I know Nix can be used for the same purpose, but I personally prefer Guix and have more experience with it.
Let’s get into the actual installation steps.
We will need to create a btrfs
subvolume for the /gnu
directory. First let’s find the system drive’s name - this can be done using the command lsblk
.
Here is an example of what the output of this command can look like:
nvme0n1 259:0 0 476,9G 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot/efi
└─nvme0n1p2 259:2 0 476,4G 0 part /gnu/store
/usr/local
/srv
/opt
/home
/gnu
/boot/writable
/boot/grub2/x86_64-efi
/boot/grub2/i386-pc
/.snapshots
/var
/root
/
In this instance, the system partition is labeled as nvme0n1p2
, but yours might be different, so write it down as you’ll soon need it!
We’ll need to create the actual subvolume now:
sudo -i # We'll log in as root for the rest of the process
mkdir /var/mnt
mount /dev/nvme0n1p2 /var/mnt -o subvolid=0 # use your parition name instead
btrfs subvolume create /var/mnt/@/gnu
umount /var/mnt
Next up we need to create the mountpoint for the subvolume.
transactional-update shell # enter the shell
mkdir /gnu
exit # exit the shell, we are now back to our host root user
exit # and we log out of the root account
Now reboot your computer. This part is important - not rebooting now will cause issues later.
The ~/gnu~ directory should exist now. (If it doesn’t it becomes impossible to proceed further, so make sure it’s there!)
Now that we have created the subvolume successfully we need to automatically mount it.
Open up the file /etc/fstab
in your editor of choice - nano
will suffice.
sudo -i
nano /etc/fstab
Now you’ll see something like this:
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe / btrfs ro 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /var btrfs subvol=/@/var,x-initrd.mount 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /usr/local btrfs subvol=/@/usr/local 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /srv btrfs subvol=/@/srv 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /root btrfs subvol=/@/root,x-initrd.mount 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /opt btrfs subvol=/@/opt 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /home btrfs subvol=/@/home 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /boot/writable btrfs subvol=/@/boot/writable 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /boot/grub2/x86_64-efi btrfs subvol=/@/boot/grub2/x86_64-efi 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /boot/grub2/i386-pc btrfs subvol=/@/boot/grub2/i386-pc 0 0
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /.snapshots btrfs subvol=/@/.snapshots 0 0
UUID=9832-F680 /boot/efi vfat utf8 0 2
overlay /etc overlay defaults,lowerdir=/sysroot/var/lib/overlay/3/etc:/sysroot/etc,upperdir=/sysroot/var/lib/overlay/4/etc,workdir=/sysroot/var/lib/overlay/4/work-etc,x-systemd.requires-mounts-for=/var,x-systemd.requires-mounts-for=/sysroot/var,x-initrd.mount 0 0
Take note of the UUID
used for the /
system (that’s the one at the top of the file). We’ll now add another line to the bottom of this file.
UUID=5805f3d4-7a45-435b-bcb4-234ce42633fe /gnu btrfs subvol=/@/gnu 0 0 # if you copy-paste make sure to replace the UUID here!
Save the file, exit and yet again reboot (the reboot here isn’t optional!).
Finally once we reboot we can verify everything is mounted correctly:
mount | grep gnu
And the output should be something similar to this:
/dev/nvme0n1p2 on /gnu type btrfs (rw,relatime,seclabel,ssd,discard=async,space_cache=v2,subvolid=269,subvol=/@/gnu)
If everything is correct, we can move on to the next step.
We can now actually install Guix into the system. The steps we are about to take are mostly copied from here but there are some minor changes.
An important note here is that the URLs might change with new Guix releases, so check you are grabbing the latest version!
First we download the Guix tarball.
sudo -i # log in as root
cd /tmp
wget "https://ftp.gnu.org/gnu/guix/guix-binary-1.4.0.x86_64-linux.tar.xz" # download the Guix binary
Now we verify its signature
wget "https://ftp.gnu.org/gnu/guix/guix-binary-1.4.0.x86_64-linux.tar.xz.sig" # download the signature
wget 'https://sv.gnu.org/people/viewgpg.php?user_id=15145' -qO - | gpg --import - # import signature
gpg --verify guix-binary-1.4.0.x86_64-linux.tar.xz.sig # and finally verify the file
If the command shows no issues, we have succesfully downloaded Guix. Take note that a warning like “This key is not certified with a trusted signature!” is normal.
We can continue the installation process - make sure you are still logged in as root!
tar --warning=no-timestamp -xf /tmp/guix-binary-1.4.0.x86_64-linux.tar.xz
mv var/guix /var/
mv gnu/store /gnu/
I also like to disable copy-on-write for the /gnu
directory as it isn’t necessary there, this should also help improve speed.
chattr +C -R /gnu
Note: The command is going to return multiple warnings. This is completely normal and nothing to worry about.
mkdir -p ~root/.config/guix
ln -sf /var/guix/profiles/per-user/root/current-guix ~root/.config/guix/current
export GUIX_PROFILE="`echo ~root`/.config/guix/current"
source $GUIX_PROFILE/etc/profile
groupadd --system guixbuild # create the group
This command spans multiple lines, make sure to copy it in its entirety!
for i in $(seq -w 1 10); # and now create the users
do
useradd -g guixbuild -G guixbuild \
-d /var/empty -s $(which nologin) \
-c "Guix build user $i" --system \
guixbuilder$i;
done
First we copy the services in the necessary location, then we enable them.
cp ~root/.config/guix/current/lib/systemd/system/gnu-store.mount ~root/.config/guix/current/lib/systemd/system/guix-daemon.service /etc/systemd/system/
systemctl enable gnu-store.mount guix-daemon
Note: if you try to immediately start the services at this point it’s going to fail. This is completely normal and nothing to worry about.
At this point we can make the binary available for all users of the system.
mkdir -p /usr/local/bin
cd /usr/local/bin
ln -s /var/guix/profiles/per-user/root/current-guix/bin/guix
It is also a good idea to make the manual available:
mkdir -p /usr/local/share/info
cd /usr/local/share/info
for i in /var/guix/profiles/per-user/root/current-guix/share/info/* ; do ln -s $i ; done
Binary substitutes allow us to avoid building packages locally, (see the official documentation)
guix archive --authorize < ~root/.config/guix/current/share/guix/ci.guix.gnu.org.pub
guix archive --authorize < ~root/.config/guix/current/share/guix/bordeaux.guix.gnu.org.pub
We are almost there, but Guix isn’t quite functional yet. To disable SELinux we need to edit a kernel boot parameter.
Open the file /etc/default/grub
with your favorite editor.
sudo nano /etc/default/grub
You should be able to find a line that looks like this:
GRUB_CMDLINE_LINUX_DEFAULT="splash=silent swapaccount=1 mitigations=auto quiet security=selinux selinux=1 enforcing=1"
Here change the enforcing
variable to 0.
GRUB_CMDLINE_LINUX_DEFAULT="splash=silent swapaccount=1 mitigations=auto quiet security=selinux selinux=1 enforcing=0"
Save the file and exit.
Now we have to regenerate the grub configuration:
sudo transactional-update grub.cfg
Reboot after this step!
After the reboot verify the status of the Guix daemon.
systemctl status guix-daemon
If everything is okay we have successfully set up Guix!
At this point I’ll also mention that Guix requires the /etc/services
file, which currently doesn’t exist by default on MicroOS.
We can easily fix this by installing the netcfg
package and linking the file in its appropriate location:
sudo transactional-update pkg in netcfg # you might need to reboot after this step
sudo ln -s /usr/etc/services /etc/services
Finally we need to configure the Guix application environment.
This is described in the Guix documentation, but here’s my quick summary:
First update our local guix profile, run this as your user:
guix pull
This might take a little while.
Next open up the $HOME/.bash_profile
file, this time as your regular user. Then fill it in like so:
if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
export GUIX_PROFILE="$HOME/.guix-profile"
export GUIX_LOCPATH="$HOME/.guix-profile/lib/locale"
export PATH="$HOME/.config/guix/current/bin:$PATH"
export SSL_CERT_DIR="$HOME/.guix-profile/etc/ssl/certs"
export SSL_CERT_FILE="$HOME/.guix-profile/etc/ssl/certs/ca-certificates.crt"
export GIT_SSL_CAINFO="$SSL_CERT_FILE"
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$HOME/.guix-profile/share"
. "$GUIX_PROFILE/etc/profile"
fi
Now log out and back in for these changes to take effect. Finally we can install the most essential Guix packages:
guix install glibc-locales nss-certs
With these steps you should be able to successfully set up Guix on MicroOS. If you find any broken steps or other problems, please post on the issues page.