Guix introduction at 37c3

Fabio Natali, 27 December 2023

Intro

This is the outline of a short session on Guix that I had the chance to organise at the 37th Chaos Communication Congress, or 37c3, in December 2023. It's not worth much, but I thought it was useful to have it here in case I want to reuse it or refer to it in the future.

What's Guix?

  • A functional, transactional, declarative package manager and operating system. We'll see what this means in a minute.
  • Inspired by the Nix project.
  • Unprivileged package management.
  • Written in Guile Scheme.
  • Free Software, part of the GNU project.

Functional

A purely functional approach at package management. This is an example package definition.

(define-public hello
  (package
    (name "hello")
    (version "2.12.1")
    (source (origin
              (method url-fetch)
              (uri (string-append "mirror://gnu/hello/hello-" version
                                  ".tar.gz"))
              (sha256
               (base32
                "086vqwk2wl8zfs47sq2xpjc9k066ilmb8z6dn0q6ymwjzlm196cd"))))
    (build-system gnu-build-system)
    (synopsis "Hello, GNU world: An example GNU package")
    (description
     "GNU Hello prints the message \"Hello, world!\" and then exits.  It
serves as an example of standard GNU coding practices.  As such, it supports
command-line arguments, multiple languages, and so on.")
    (home-page "https://www.gnu.org/software/hello/")
    (license gpl3+)))

On a system where Guix is already installed, use guix search PACKAGE to search for packages, then guix edit PACKAGE to look at the package definition.

guix search less
guix edit less

Transactional

Package management operations via guix package and related commands. Operations are transactional, i.e. they either succeed or nothing happens, and they can be rolled-back.

Some examples:

guix pull
guix package --upgrade
guix install emacs
guix package --list-generations
guix install vim
guix package --list-generations
guix package --roll-back
guix package --switch-generation

Declarative

A base system definition

A base system definition can be created as follows.

(define-module (operating-system-base)
  #:use-module (gnu)
  #:export (operating-system-base))

(define operating-system-base
  (operating-system
   (host-name "host")
   (timezone "Europe/London")
   (locale "en_US.UTF-8")
   (bootloader (bootloader-configuration
                (bootloader grub-bootloader)
                (targets '("/dev/vda"))))
   (file-systems (cons
                  (file-system
                   (device "/dev/vda2")
                   (mount-point "/")
                   (type "ext4"))
                  %base-file-systems))
   (users (cons
           (user-account
            (name "user")
            (group "users")
            (password (crypt "password" "salt"))
            (home-directory "/home/user"))
           %base-user-accounts))))

operating-system-base

Build and run a system definition 1/2

Build a system image based on the definition above and run it with QEMU. This can be done with the following helper command.

$(guix system vm build/operating-system-base.scm)

When experimenting with the system in QEMU, note that ping will not work, this is expected because of -nic user.

guix pull also will not work, this is expected because the store is mounted read-only. To have a fully working VM, build the image, make it writeable, and launch it with QEMU, in separate steps, see below and this page on the manual.

Related commands: guix system image, guix system reconfigure.

A slightly more advanced system definition

This is a slightly more advanced system definition, where we add some packages and some services.

(define-module (operating-system-plus)
  #:use-module (gnu)
  #:use-module (gnu packages admin)
  #:use-module (gnu packages emacs)
  #:use-module (gnu services networking)
  #:export (operating-system-plus))

(define operating-system-plus
  (operating-system
   (host-name "host")
   (timezone "Europe/London")
   (locale "en_US.UTF-8")
   (bootloader (bootloader-configuration
                (bootloader grub-bootloader)
                (targets '("/dev/vda"))))
   (file-systems (cons
                  (file-system
                   (device "/dev/vda2")
                   (mount-point "/")
                   (type "ext4"))
                  %base-file-systems))
   (users (list
           (user-account
            (name "user")
            (group "users")
            (password (crypt "password" "salt"))
            (home-directory "/home/user"))
           (user-account
            (name "root")
            (group "users")
            (password (crypt "password" "salt"))
            (home-directory "/root"))))
   (packages (cons* emacs cloud-utils %base-packages))
   (services (cons* (service dhcp-client-service-type) %base-services))))

operating-system-plus

This is another example where we include a typical desktop environment.

(define-module (operating-system-desktop)
  #:use-module (gnu)
  #:use-module (gnu packages admin)
  #:use-module (gnu packages emacs)
  #:use-module (gnu services desktop)
  #:export (operating-system-plus))

(define operating-system-desktop
  (operating-system
   (host-name "host")
   (timezone "Europe/London")
   (locale "en_US.UTF-8")
   (bootloader (bootloader-configuration
                (bootloader grub-bootloader)
                (targets '("/dev/vda"))))
   (file-systems (cons
                  (file-system
                   (device "/dev/vda2")
                   (mount-point "/")
                   (type "ext4"))
                  %base-file-systems))
   (users (list
           (user-account
            (name "user")
            (group "users")
            (password (crypt "password" "salt"))
            (home-directory "/home/user"))))
   (packages (cons* emacs cloud-utils %base-packages))
   (services %desktop-services)))

operating-system-desktop

And this is what the system looks like if booted with guix system vm.

guix-qemu.jpg

Figure 1: A Guix system run in QEMU.

Build and run a system definition 2/2

To have a fully working VM, build the image, make it writeable, and launch it with QEMU, in separate steps, see below.

orig=$(guix system image --image-type=qcow2 build/operating-system-plus.scm)
dest=/tmp/qemu-image
cp $orig $dest
chmod +w $dest
qemu-img resize -f qcow2 $dest +10G
qemu-system-x86_64 \
    -enable-kvm \
    -m 8192 \
    -nic user,model=virtio-net-pci \
    -device virtio-blk,drive=myhd \
    -drive if=none,file=$dest,id=myhd

Once deployed to a machine, an image can be resized so as to take all the available space. We use growpart, which is part of cloud-utils:

Log in as root and use growpart and resize2fs as follows.

growpart /dev/vda 2
resize2fs /dev/vda2

Now log in as a user. It's finally possible to install new software:

guix install hello

How it works

$ guix describe
Generation 274  Dec 23 2023 21:43:58    (current)
  guix a819663
    repository URL: https://git.savannah.gnu.org/git/guix.git
    branch: master
    commit: a8196632647b343f8e03c8f12fbdc0cc84ff90f6

A quick introduction to: channels, package definitions, guix pull, package derivations, binaries in the store, path reconfiguration.

The Store

What is the Store? Let's have a look at the /gnu/store folder.

$ ls /gnu/store | tail
zzxqah5a58ns8dpsf0w6ka24vr562qns-valgrind-3.20.0.tar.bz2.drv
zzyfc0d0z5h5c64n9bxarvkx3y48s2zd-rust-miniz-oxide-0.6.4.tar.gz.drv
zzysk08z851qhwi42l6lq1f8nyn9k5vc-npth-1.6.drv
zzyywykw7kriln18rxqd82f0k5kidla7-bash-static-5.1.16/
zzyzh5s2l4h5g47b8rwvnv8r7h8l4656-libffi-3.4.4.drv
zzz15qg4lk5w5rgiiahaabz614vhy02x-enchant-1.6.0.tar.gz.drv
zzzgxg4sdfim3crvarrfa9pm9zl30kbw-guix-system-tests-modules/
zzzpfppi800znv8vjd2ral25p3lzzz4l-ghc-strict-0.4.0.1.drv
zzzv5j1c8yridwl52gvzlk5f2w4l78zl-ghc-generic-deriving-1.14.2.drv
zzzyi178qx3pvmrk4sbdkr8mjqxama0r-cppcheck-2.10.3-checkout.drv

Let's see how any command links back to an item in the store.

$ which less
/home/user/.guix-home/profile/bin/less
$ namei $(which less)
f: /home/user/.guix-home/profile/bin/less
 d /
 d home
 d user
 l .guix-home -> /gnu/store/vx57sj8dzzl234y49hpmmk1jvaxjr6cs-home
   d /
   d gnu
   d store
   d vx57sj8dzzl234y49hpmmk1jvaxjr6cs-home
 l profile -> /gnu/store/hms2bgscfj36sc5mm7a47j9msxqgkz26-profile
   d /
   d gnu
   d store
   d hms2bgscfj36sc5mm7a47j9msxqgkz26-profile
 d bin
 l less -> /gnu/store/30zfbjasrsk2wg8nhsd1xgi3q3n9796z-less-608/bin/less
   d /
   d gnu
   d store
   d 30zfbjasrsk2wg8nhsd1xgi3q3n9796z-less-608
   d bin
   - less

What's in a package?

$ tree $(guix build hello) | head
/gnu/store/6fbh8phmp3izay6c0dpggpxhcjn4xlm5-hello-2.12.1
├── bin
│   └── hello
├── etc
│   └── ld.so.cache
└── share
    ├── doc
    │   └── hello-2.12.1
    │       └── COPYING
    ├── info
    ...

Package dependencies

Guix provides a nice utility to visualise software dependencies.

guix graph coreutils | guix shell graphviz -- dot -Tjpg

coreutils.jpg

Figure 2: Coreutils dependencies.

guix graph vim | guix shell graphviz -- dot -Tjpg

vim.jpg

Figure 3: Vim dependencies.

guix graph emacs-minimal | guix shell graphviz -- dot -Tjpg -Gdpi=10

emacs.jpg

Figure 4: Emacs dependencies (emacs-minimal).

How many packages available?

As of December 2023, the website Repology reports Guix as the 5th distribution for number of packaged projects, with a total 26000 plus packages.

Is your favourite software packaged under Guix already? Find out here.

Guix environments with guix shell

For instance:

guix shell python python-numpy -- python3

Containerisation:

guix shell --container python

A more advanced example to run Chromium with some lightweight isolation:

guix shell \
    --container \
    --network \
    --no-cwd \
    ungoogled-chromium \
    --preserve="^DISPLAY$" \
    --expose=/tmp/.X11-unix \
    --expose=/home/user/.Xauthority \
    -- chromium

A similar example to test Emacs with a particular font:

guix shell \
    --no-cwd \
    --container \
    --expose=/tmp/.X11-unix \
    --expose=/home/user/.Xauthority \
    --preserve="^DISPLAY$" \
    emacs fontconfig font-google-noto font-google-noto-emoji \
    -- emacs

Guix for reproducibility

Let's refer to this paper: Vallet, Michonneau, Tournier, Toward practical transparent verifiable and long-term reproducible research using Guix.

[…] open tools like Guix can be used by any scientist to share their environment and allow peers to reproduce it.

guix-reproducibility-nature-article.jpg

Figure 5: Guix for scientific research reproducibility. Image: Vallet, Michonneau, Tournier.

Travelling in time is now possible, thanks to guix time-machine.

guix time-machine \
    --commit=769b96b62e8c09b078f73adc09fb860505920f8f \
    -- shell gcc-toolchain

Guix as a package manager on a foreign distribution

E.g. on Debian, Ubuntu (with apt install guix), or Arch (with pacman guix-installer or some variation thereof). On a foreign distro, after installation it may be necessary to configure some environment variable, see the manual for details.

After that, it will be possible to install Guix packages alongside your main operating system's package list.

Example on a Debian VPS

On the remote machine:

apt update
apt install guix
useradd -m -d /home/user -s /bin/bash user
passwd user
mkdir /home/user/.ssh

On the local machine:

scp /home/user/.ssh/id_rsa.pub root@IP-HERE:/home/user/.ssh/authorized_keys

Back on the remote machine:

chown -R user:user /home/user/.ssh
chmod 700 /home/user/.ssh
chmod 600 /home/user/.ssh/authorized_keys

After that, log in as a user and follow these instructions.

Guix Home

Users can declare all the packages and services that should be installed and configured for them, in their home/environment. See the relevant page on the manual.

For instance:

(use-modules (gnu home)
             (gnu home services)
             (gnu home services shells)
             (gnu services)
             (gnu packages admin)
             (guix gexp))

(home-environment
 (packages (list htop tree))
 (services
  (list
   (service home-bash-service-type
            (home-bash-configuration
             (guix-defaults? #t)
             (bash-profile
              (list (plain-file
                     "bash-profile"
                     "export HISTFILE=$XDG_CACHE_HOME/.bash_history")))))

   (simple-service 'test-config
                   home-xdg-configuration-files-service-type
                   (list `("test.conf"
                           ,(plain-file "tmp-file.txt"
                                        "the content of
                                          ~/.config/test.conf")))))))

Guix Home in a container

Guix Home definitions can be instantiated and tested in a container, as follows.

guix home container build/home-configuration-base.scm

As expected, no command is available except htop, tree, and the basic Bash utilities. Have a look at the files available with tree -pah.

A more complicated example

Info on the current Guix Home

$ guix home describe
Generation 813  Dec 23 2023 21:45:08    (current)
  file name: /var/guix/profiles/per-user/user/guix-home-813-link
  canonical file name: /gnu/store/5jvxybkjh2i7gg4ad1s61qgpql9wjy7z-home
  channels:
    guix:
      repository URL: https://git.savannah.gnu.org/git/guix.git
      branch: master
      commit: a8196632647b343f8e03c8f12fbdc0cc84ff90f6
  configuration file: /gnu/store/...-configuration.scm

How to contribute

In (very) approximate order of difficulty:

  • Simply using Guix, finding bugs, and submitting bug reports!
  • Being part of the community, e.g. mailing lists and IRC.
  • Updating existing package definitions or contributing new ones.
  • Helping with code review, see this blog post.
  • Contributing with patches (other than package definitions).
  • Becoming a committer.

Resources

Revision 0133bba.