Keeping one’s home tidy

How much effort to recreate your work environment when you switch to a new machine? What would it take to roll back to your previous environment once you’ve noticed a program no longer behaves as expected? What about sharing your environment with friends of yours? These are some of the things that Guix Home, which landed in Guix as a “technology preview” in September 2021, aims to make effortless, reliable, and fun.

In a nutshell, Guix Home brings the fully declarative configuration of Guix System to home directories. With Guix System, users and administrators provide a configuration file that defines the operating system configuration; with Guix Home, users provide a configuration file that defines the configuration of their work environment in their home directory—their home environment. That configuration is meant to stand alone, to describe all the relevant aspects of your work environment. But what exactly goes in a home environment?

“Dot files” don’t live in a vacuum

Among seasoned Unix-style users, we often equate “home environment” with “dot files”—configuration files in our home directory, from ~/.bashrc and ~/.ssh/config to ~/.emacs and everything under ~/.config. These files are precious and many store them under version control, to keep track of changes made to their configuration. That’s a good idea, but is that all it takes to describe the home environment? To roll back to a previous version?

Of course not. Dot files don’t exist in a vacuum; at the very least, your home environment is not just a set of dot files, but also a set of installed packages. They work together: if you upgrade a package, the corresponding dot file might need to be adjusted; if a package is missing, its dot file is not of any use. Sometimes a home environment contains additional things: daemons (programs that run in the background), or periodically executed jobs.

Guix Home goes beyond dot files: it lets you declare and instantiate all these aspects that make up your home environment.

Genesis

Guix Home was initially developed by Andrew Tropin as part of the rde project; it was integrated in Guix proper six months ago. I am writing this as an adopter and contributor, but there were a number of earlier adopters and earlier contributors. In fact, despite being still very much under development, the tool has already attracted a number of excited users eager to find a way to keep their home tidy!

The idea of writing down a declaration of your home environment that you can reproduce anytime is a natural followup to everything Guix does—you could already declare a package set in a manifest or even a complete operating system. It had been floating around, in Nix land with Home Manager and in Guix land with the now-defunct Guix Home Manager by Julien Lepiller. The latter was similar to today’s Guix Home, but went one step further by making your home directory read-only—yes, read-only! The main advantage is that it would ensure statelessness—you’d be sure that absolutely all your home configuration is under Guix Home Manager’s control; sub-directories containing mutable data would have to be explicitly declared. The downside is that it raised the barrier to entry: you’d have to either switch entirely, or not use it at all. Guix Home takes a more pragmatic approach and happily coexists with configuration managed “the old way”.

Getting started

To get started, you need a Home configuration file. There’s documentation, but as always, starting from a blank page is a bit intimidating. So instead of starting from a blank page, you can let guix home import generate an initial config for you:

guix home import ~/src/guix-config

This will create the ~/src/guix-config directory and populate it with a bunch of files among which home-configuration.scm along these lines:

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

(home-environment
 (packages
  (map (compose list specification->package+output)
       (list "emacs-geiser-guile"
             "emacs-geiser"
             "pinentry-emacs"
             "emacs-exwm"
             "gnome-maps"
             "pipe-viewer"
             "emacs"
             "pavucontrol"
             "git"
             "xterm"
             "qemu"
             "openssh")))
 (services
  (list (service home-bash-service-type
                 (home-bash-configuration
                  (aliases
                   '(("grep" . "grep --color=auto")
                     ("ll" . "ls -l")
                     ("ls" . "ls -p --color=auto")
                     ("qemu" . "qemu-system-x86_64 -enable-kvm -m 512")
                     ("rm" . "rm --one-file-system")))
                  (bashrc
                   (list (local-file "/home/charlie/src/guix-config/.bashrc" 
                                     "bashrc")))
                  (bash-profile
                   (list (local-file
                          "/home/charlie/src/guix-config/.bash_profile"
                          "bash_profile"))))))))

guix home import automatically added the packages of ~/.guix-profile to the packages field. Because I’m using Bash, it also added an instance of home-bash-service-type with aliases extracted from my ~/.bashrc; it also made copies of ~/.bashrc and ~/.bash_profile and refers to them.

Now that I have an initial configuration, I can first test it in an isolated container:

guix home container ~/src/guix-config/home-configuration.scm

This command gives an interactive shell in a container where my home environment, as declared in home-configuration.scm, is deployed. There I can see my home directory as it would look like if I deploy my home environment “for real”: I can see my ~/.bashrc and co., I can check that all the packages declared are in $PATH and visible in ~/.guix-home, and so on. And all this is safe: my actual home directory has been left unchanged!

Once satisfied with my configuration, I can instantiate it:

guix home reconfigure ~/src/guix-config/home-configuration.scm

At that point, my actual home directory corresponds to that configuration. Some of my dot files are now provided by Guix Home, and thus they’re symbolic links (“symlinks”) to their read-only copy in /gnu/store:

$ ls -l ~/.bashrc ~/.bash_profile
lrwxrwxrwx 1 charlie users 56 Mar  7 15:46 /home/charlie/.bash_profile -> /gnu/store/lpdydssyyxx9n0xvp2jmv7yqgyr2pcg3-bash_profile
lrwxrwxrwx 1 charlie users 50 Mar  7 15:46 /home/charlie/.bashrc -> /gnu/store/kxc0j4i05sib04vf92nr8xxkb8isdfn7-bashrc

But don’t worry: before creating those symlinks, guix home reconfigure created backups of existing files under ~/TIMESTAMP-guix-home-legacy-configs-backup, where TIMESTAMP is a Unix-style timestamp.

And voilà, I have my first Guix Home generation!

$ guix home describe
Generation 1    Mar 07 2022 15:46:20   (current)
  file name: /var/guix/profiles/per-user/charlie/guix-home-1-link
  canonical file name: /gnu/store/qr1c5jpfrj815ncv6yr2lfdgs8nq8kkn-home
  channels:
    guix:
      repository URL: https://git.savannah.gnu.org/git/guix.git
      branch: master
      commit: 3ac1366648f933f7244c2d0b9926f7ba5d92a113
  configuration file: /gnu/store/xfgasfms9rhhigyj7i8za77zpqx6zbhn-configuration.scm

guix home describe shows provenance tracking we know and love from Guix System: all the info we need to redeploy the same home environment elsewhere, or at a different point in time. It’s also information guix home reconfigure relies on to make sure you never accidentally downgrade you home environment to an older Guix revision.

Going further

Alright, at this point, you might be thinking that it’s a lot of fuss but the “only” benefit over dot files under version control is that guix home also takes care of installing packages. Guix Home really shines once you use higher-level services, and when you start composing services together.

To the example above, in the services field, we can add a service declaration that runs Redshift, a program that adjusts the display color temperature according to the time of day:

(service home-redshift-service-type
         (home-redshift-configuration
          (location-provider 'manual)
          (latitude 35.81)    ;northern hemisphere
          (longitude -0.80))) ;west of Greenwich

The effect is that, as soon as we log in, under Xorg, Redshift will be started in the background as a Shepherd service. The Home-generated ~/.profile takes care of spawning shepherd, which in turn spawns the redshift service:

$ herd status
Started:
 + root
 + redshift

We gained another thing here: a consistent, unified configuration language. Instead of learning Redshift’s configuration file format, we define a home-redshift-configuration record, right in Scheme. Under the hood, that configuration is converted into Redshift’s file format; any error is caught at configuration time, when running guix home reconfigure, and we can be sure that Redshift is passed a valid configuration file.

We can similarly define a periodic mcron job, for example one that updates a GNU Idutils search database (that’s a pretty convenient and speedy way to look for code or documents!):

(simple-service 'idutils home-mcron-service-type
                ;; Every day at 12:15 and 19:15.
                (list #~(job '(next-minute-from (next-hour '(12 19)) '(15))
                             (string-append #$idutils "/bin/mkid \
-o $HOME/.idutils/src.db $HOME/src"))))

Again, guix home creates a Shepherd service that start mcron with a configuration file containing definitions for periodic jobs, which we can inspect via herd:

$ herd schedule mcron | head -5
Sun Mar 20 19:15:00 2022 +0000
/gnu/store/2d026nan309qkci968k8gpa8fcv9q4mv-idutils-4.6/bin/mkid -o $HOME/.idutils/src $HOME/src

Mon Mar 21 12:15:00 2022 +0000
/gnu/store/2d026nan309qkci968k8gpa8fcv9q4mv-idutils-4.6/bin/mkid -o $HOME/.idutils/src $HOME/src

Services, composed

If you already use Guix System, all the above certainly looks familiar: Guix Home builds upon the service framework that powers Guix System; Home services are defined in the (gnu home services …) module tree.

That framework lets us define relations among “services”, in a broad sense, and how services extend each other—in the example above, redshift and mcron both extend shepherd by giving it a daemon to take care of. We can see those relations at play by running:

guix home extension-graph home-configuration.scm

… which, for the configuration described above, gives a graph that looks like this:

Extension graph for home services.

We see redshift, mcron, and shepherd, but we also see lower-level services that guix home instantiates for us, such as the profile service which takes care of deploying packages listed in the packages field under ~/.guix-home/profile. Each arrow denotes a service extension. You can read more (and view more!) about service composition. To satisfy our math and functional-programming geek audience, we should mention that service types and their extension operation form a monoid.

What’s next?

Let’s be clear: Guix Home is pretty new and chances are that guix home search—the command to search for services by keyword—won’t give you the service you’re looking for. There’s also a bunch of open questions left, such as how to reuse services initially defined for Guix System in cases where they could be equally useful in Guix Home—Syncthing, for example.

But while it’s still a “technology preview”, it’s already a tool that tinkerers can play with and benefit from. Patches adding new services have already been proposed; maybe your favorite service is next? Consider contributing.

With a new release and ten-year anniversary coming up, we’re happy to celebrate with a tool that extends the reach of declarative and reproducible deployment!

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 Hurd or the Linux kernel, or it can be used as a standalone operating system distribution for i686, x86_64, ARMv7, AArch64 and POWER9 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.