Sebastian's personal website

Running guix package manager on top of Fedora Silverblue, updated instructions

Written by Sebastian Dümcke on
Tags:

I have received reports that my previous instructions on how to install the guix package manager on Fedora Silverblue ceased to work. This was due to a change in Fedora Silverblue 42/43 enabling composefs. I took some time last weekend to find a solution and publish new instructions in this post. In addition to working on the current version of Silverblue, these instructions also allow running guix without turning off SELinux and migrate to the new unprivileged daemon for an increase in security! Let’s dive right in.

Instructions

All these steps have been performed on a fresh Silverblue 43 install. If you are upgrading from a prior version with guix installed you might want to clean up the prior changes (most likely the guix-daemon.service file) and remove the guixbuild users etc…

  1. disable composefs using method from:https://github.com/DeterminateSystems/nix-installer/issues/1445#issuecomment-2856334377 This is certainly the part most likely to break in the future. But it works in the current version 43 of Silverblue.

    sudo tee /etc/ostree/prepare-root.conf <<'EOL'
    [composefs]
    enabled = yes
    [root]
    transient = true
    EOL
    
    sudo rpm-ostree initramfs-etc --reboot --track=/etc/ostree/prepare-root.conf 
    
  2. add the unprivileged build user and group required by the new guix-daemon

    sudo groupadd --system guix-daemon
    sudo useradd -g guix-daemon -G guix-deamon,kvm -d /var/empty -s "$(command -v nologin)" -c "Unpriviledged Guix Daemon User" guix-daemon
    
  3. Download the latest binary release from guix, untar the files, copy to their final location and change ownership to guix_daemon user

     ARCHIVE=wn70p66a5lvk3sj0n0f9rcd5fp6f3s25-guix-binary.tar.xz
     tar xvf $ARCHIVE
    #mv gnu/store to /var/gnustore and guix to /var/guix
    sudo mv gnu/store /var/gnustore
    sudo mv var/guix /var/
     sudo chown -R guix-daemon:guix:daemon /var/gnustore /var/guix
     sudo chown -R root:root /var/guix/profiles/per-user/root
    
  4. create log directory for guix

    sudo mkdir -p /var/log/guix
    sudo chown guix-daemon:guix-daemon /var/log/guix
    sudo chmod 755 /var/log/guix
    
  5. adapt and install the SELinux context: This might not be required in the future if a patch is committed to the guix repository. The issue is tracked here.

    sed '243s/write)/write mounton)/' $(find /var/gnustore -name guix-daemon.cil | head -n 1) > guix-daemon.cil
    sudo semodule -i guix-daemon.cil
    
  6. install the systemD files for mounting the store and starting the guix-daemon
    • Service unit to create the mount point at root level. This is the part that requires disabling composefs in the step 1.

      #/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 /
      
    • Service unit to mount /var/gnustore/ to /gnu/store

      #/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
      
    • Service unit to start the guix-daemon adapted to our Silverblue scenario

      #/etc/systemd/system/guix-daemon.service
      [Unit]
      Description=Build daemon for GNU Guix
      
      #ensure store is mounted under /gnu/store for profile links to work
      After=gnu-store.mount
      Requires=gnu-store.mount
      
      [Service]
      ExecStart=/var/guix/profiles/per-user/root/current-guix/bin/guix-daemon \
          --discover=no \
          --substitute-urls='https://bordeaux.guix.gnu.org https://ci.guix.gnu.org'
      Environment='GUIX_STATE_DIRECTORY=/var/guix' 'GUIX_LOCPATH=/var/guix/profiles/per-user/root/guix-profile/lib/locale' LC_ALL=en_US.utf8
      
      # Run under a dedicated unprivileged user account.
      User=guix-daemon
      
      # Bind-mount the store read-write in a private namespace, to counter the
      # effect of 'gnu-store.mount'.
      PrivateMounts=true
      BindPaths=/var/gnustore:/gnu/store
      # Disable host file system mount propagation to keep service view of the
      # store read-write after 'gnu-store.mount' makes it read-only system-wide.
      MountFlags=private
      # Mitigate race condition between guix-daemon and 'gnu-store.mount'.
      # Dependent units will only start after daemon binary is started AND THUS
      # the mount point is acquired in a private namespace.
      Type=exec
      
      # Provide the CAP_CHOWN capability so that guix-daemon can create and chown
      # /var/guix/profiles/per-user/$USER and also chown failed build directories
      # when using '--keep-failed'.  Note that guix-daemon explicitly drops ambient
      # capabilities before executing build processes so they don't inherit them.
      AmbientCapabilities=CAP_CHOWN
      
      StandardOutput=journal
      StandardError=journal
      
      # Work around a nasty systemd ‘feature’ that kills the entire process tree
      # (including the daemon!) if any child, such as cc1plus, runs out of memory.
      OOMPolicy=continue
      
      # Despite the name, this is rate-limited: a broken daemon will eventually fail.
      Restart=always
      
      # See <https://lists.gnu.org/archive/html/guix-devel/2016-04/msg00608.html>.
      # Some package builds (for example, go@1.8.1) may require even more than
      # 1024 tasks.
      TasksMax=8192
      
      [Install]
      WantedBy=multi-user.target
      
  7. Mount the gnu store, remount it rw and label the files for SELinux

    sudo systemctl start gnu-store.mount
    sudo mount -o remount,rw /gnu/store
    sudo restorecon -R /gnu /var/guix
    sudo umount /gnu/store
    
  8. Finally we can enable and start the guix-daemon: sudo systemctl --now enable guix-daemon
  9. Last we need to authorize the substitutes and pull from the root guix to generate the local guix version:

    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
    

Do not forget to source the profile as indicated by the message in the shell.

Conclusion

With these changes we can use guix on Fedora Silverblue again. However, the Silverblue project is still rapidly evolving. It is my understanding that they are migrating away from OSTree to bootc and might implement further changes that will again break these instructions. If you find any issues, do not hesitate to write me an email (code at sam-d.com), I enjoy feedback from my readers.

I had other troubles updating from 41 to 42 related to package overlays that have kept me on version 41 for far too long. My main concern in using Silverblue compared with other distributions (and guix system) is to be able to upgrade without fear and having a system that ’just works’. This has been mainly true for Silverblue aside from these recent hiccups. I have looked into other immutable distributions (nitrux, VanillaOS, blandOS, arkane Linux, SUSE) however Silverblue still seems to best respond to my needs. I am happy to have a running system with guix again.