Running a Guix Xfce Desktop on CentOS 7

This tutorial will show how to run a fully fledged Xfce desktop environment installed with Guix on top of an existing GNU/Linux distribution. This guide uses CentOS 7 as the base operating system and assumes that Xorg is already configured and running on VT2 under a different user account.

We will borrow Xorg and xinit from the host distribution and run Guix Xfce on virtual terminal 4 as user alice. No system-wide configuration files need to be touched (apart from the Guix install), but we do make a couple of changes for convenience.

From scratch to Xfce

If Guix is not already installed, go grab the installation script and run it as sudo bash guix-install.sh.

The script creates /gnu/store/ and /var/guix/ and configures a system service for guix-daemon. By default the daemon runs from the root users Guix; we won't be using the root account in this guide, so let's start by making the guix-daemon service refer to our local user alice instead.

sudo sed -i 's/root/alice/' /etc/systemd/system/guix-daemon.service

Now every time Alice runs 'guix pull', the daemon gets updated too. If you installed Guix just now, make sure to run guix pull before proceeding further.

Next we'll add some lines to Alices .bash_profile to set up PATH and related variables:

~/.bash_profile:

GUIX_PROFILE="${HOME}/.guix-profile"
[[ -L "${GUIX_PROFILE}" ]] && . "${GUIX_PROFILE}/etc/profile"

export PATH="${HOME}/.config/guix/current/bin:${PATH}"
export INFOPATH="${HOME}/.config/guix/current/share/info:${INFOPATH}"
export MANPATH="${HOME}/.guix-profile/share/man:/usr/share/man"

export XDG_CONFIG_DIRS="${HOME}/.desktop-profile/etc/xdg:${HOME}/.guix-profile/etc/xdg"
export XDG_DATA_DIRS="${HOME}/.desktop-profile/share:${HOME}/.guix-profile/share"

This will look familiar if you have used Guix on a foreign distribution before. The XDG_ variables tell desktop environments where to look for installed programs and things like autostart files: we want minimal interference from the host system, so we "hard code" them to refer to just our Guix profiles.

We will install Xfce and related programs to a separate Guix profile that can be updated and rolled back independently of the main user profile. That allows us to distinguish between "stable desktop environment" and "end user packages". To keep things manageable, we create a manifest for the desktop profile that can be kept in version control, and which allows us to reproduce the exact same environment in the future (even on a different computer!).

~/desktop-manifest.scm:

(specifications->manifest
 '("xfce" "xfce4-session" "xfconf" "xfce4-battery-plugin"
   "pulseaudio" "xfce4-volumed-pulse" "xfce4-notifyd"
   ;; Helpful graphical programs.
   "mousepad" "orage"
   ;; System configuration utilities.
   "xbacklight" "pavucontrol" "stow"
   ;; For HTTPS access.
   "nss-certs"
   ;; These utilities are provided by the host, but we want the Guix versions
   ;; because they are likely better integrated and up to date.
   "fontconfig" "bash-completion" "gnupg" "man-db" "git"))

Create the initial profile generation:

guix package -p ~/.desktop-profile -m ~/desktop-manifest.scm

That installs a union of all packages listed in the manifest to ~/.desktop-profile, and creates a script we will use to "activate" it later. To update this profile, simply invoke the same command again after running guix pull or modifying the manifest.

Before Xfce can be started, we need to create a configuration file for the X server to ensure the host executable is used, and we will tell it to to stay on virtual terminal 4. We also create a .xinitrc script that automatically starts Xfce every time xinit is invoked.

~/.xserverrc:

exec /usr/bin/Xorg -novtswitch -nolisten tcp "$@" vt$XDG_VTNR

~/.xinitrc:

#!/bin/sh

# Get the default xinit configuration for CentOS.
. /etc/X11/xinit/xinitrc-common

exec startxfce4

.xinitrc needs to be executable:

chmod +x ~/.xinitrc

Now let's activate the desktop profile and start the X server, using ":1" as DISPLAY (remember that we have another X server running on VT2, occupying the default ":0" display).

GUIX_PROFILE=~/.desktop-profile
source ~/.desktop-profile/etc/profile
xinit -- :1

Cool, we're in Xfce! Let's open a terminal and install a browser & some fonts:

guix install icecat font-liberation font-dejavu

To make the newly installed fonts available right away we need to invoke fc-cache:

fc-cache -rv

Finally, we'll configure the shell to source scripts installed by Guix so that bash completions and similar work, by adding these lines at the end of .bashrc:

~/.bashrc:

# Source the Guix shell configuration directories, for vte.sh and bash completions.
GUIX_PROFILES=("${HOME}/.desktop-profile"
               "${HOME}/.guix-profile"
               "${HOME}/.config/guix/current")
for profile in "${GUIX_PROFILES[@]}"; do
    for dir in "${profile}/etc/bash_completion.d" "${profile}/etc/profile.d"; do
        if [[ -d "${dir}" ]]; then
            for f in "${dir}"/*; do
                . $f
            done
        fi
    done
done

Phew! It took some work, but by now you should have a working Xfce desktop environment, with bash completions and all. If you are content with starting it manually, skip to "final tweaks" below. Otherwise, read on.

(If you do not have a working desktop after following these steps, please email guix-devel@gnu.org so we can adjust the tutorial!)

Starting Xfce automatically on boot

We can configure our login shell to run xinit every time we log in to VT4 by adding these lines at the end of ~/.bash_profile:

# Start Xorg on display :1 when logged in to VT4, unless DISPLAY is already set.
if [[ -z "${DISPLAY}" && "${XDG_VTNR}" == 4 ]]; then
    GUIX_PROFILE="${HOME}/.desktop-profile"
    source "${HOME}/.desktop-profile/etc/profile"
    exec xinit -- :1
fi

To avoid the need for typing username and password at the console, instruct the getty service for TTY4 to automatically log in user 'alice':

/etc/systemd/system/getty@tty4.service.d/override.conf:

[Unit]
After=graphical.target

[Service]
# Delay for a few seconds, to ensure the Xorg server on VT2 starts first.
ExecStartPre=/bin/sleep 3
ExecStart=
ExecStart=-/sbin/agetty --autologin alice --noclear %I $TERM
Restart=on-success

Now just switching to VT4 will start Xfce! To do this when the system boots, simply enable the getty@tty4 service:

sudo systemctl enable getty@tty4.service

Final tweaks

Some issues were found during usage of the Xfce environment. Launching programs from the file manager failed because gio-launch-desktop was unavailable, and xfce4-terminal complained that the shell function __vte_prompt_command was not found.

These problems will be fixed in Guix eventually, but for now we'll work around them by adding the glib:bin and vte packages to our manifest:

~/desktop-manifest.scm:

(specifications->manifest
 '("xfce" "xfce4-session" "xfconf" "xfce4-battery-plugin"
   ...
   "glib:bin"                  ;for 'gio-launch-desktop'
   "vte"))                     ;for vte.sh, required by xfce4-terminal

We also found that closing the lid would not send the system to sleep, even though xfce4-power-manager --dump showed no problems. To work around it, we told systemd to ignore any "inhibitors" and take care of lid handling itself:

/etc/systemd/logind.conf:

HandleLidSwitch=suspend
LidSwitchIgnoreInhibited=yes

Additionally it is strongly recommended to enable the name service cache daemon if not already running. On CentOS this can be done by:

sudo yum install nscd

Bonus section: Installing programs with a custom build of Qt

One additional issue was that Qt programs did not work due to the stock CentOS kernel being too old. Specifically it lacks the renameat2() system call. Luckily Qt can be configured not to use it. A patch has been submitted to Guix, but since we are in a hurry, we will add a procedure to our manifest so we can use Qt programs (here wpa-supplicant-gui) until the Guix fix is merged:

~/.desktop-manifest.scm:

(use-modules (guix packages)
             (guix utils)
             (gnu)
             (gnu packages admin)
             (gnu packages qt))

(define qtbase/fixed
  (package/inherit
   qtbase
   (arguments
    (substitute-keyword-arguments (package-arguments qtbase)
      ((#:phases phases)
       `(modify-phases ,phases
          (add-after 'unpack 'disable-renameat2
            (lambda _
              ;; Mimic the '-no-feature-renameat2' configure flag.
              (substitute* "src/corelib/configure.json"
                (("config\\.linux && tests\\.renameat2")
                 "false"))
              #t))))))))

(define with-fixed-qt
  ;; This procedure recursively rewrites any references to 'qtbase'
  ;; with our patched version.
  (package-input-rewriting `((,qtbase . ,qtbase/fixed))))

(packages->manifest
 (append (list (with-fixed-qt wpa-supplicant-gui))
         (map specification->package
              '("xfce" "xfce4-session" "xfconf" "xfce4-battery-plugin"
                ...))))

...and now wpa_gui works after installing the new manifest!

Acknowledgements

Special thanks to Ocean Space Acoustics AS for sponsoring this work.

About GNU Guix

GNU Guix is a transactional package manager and an advanced distribution of the GNU system that respects user freedom. Guix can be used on top of any system running the kernel Linux, or it can be used as a standalone operating system distribution for i686, x86_64, ARMv7, and AArch64 machines.

In addition to standard package management features, Guix supports transactional upgrades and roll-backs, unprivileged package management, per-user profiles, and garbage collection. When used as a standalone GNU/Linux distribution, Guix offers a declarative, stateless approach to operating system configuration management. Guix is highly customizable and hackable through Guile programming interfaces and extensions to the Scheme language.

Related topics:

Foreign distribution Xfce

Unless otherwise stated, blog posts on this site are copyrighted by their respective authors and published under the terms of the CC-BY-SA 4.0 license and those of the GNU Free Documentation License (version 1.3 or later, with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts).