Daniel Siepmann - Coding is Art

Blog Post

Migrating to nix home-manager

Published: , Updated:

Topics: nix, unix

Introduction

I'm using Ubuntu LTS for some years now, coming from macOS with Homebrew beforehand.

Right now I'm switching parts of my setup to nix home-manager. Nix itself is a package manager and language to describe how to install packages. home-manager is a community project built on top of the language to describe the setup of a user (home).

This post will give a short overview of the benefits and my new setup. I'm still a beginner in the whole nix ecosystem. Still, I already enjoy it on a daily basis.

I'll try to explain the different involved pieces step by step. First, nix itself as a language and package manager. Followed by nixpkgs which provides a lot of pre-defined packages ready for installation. Before coming to home-manager which I use right now to maintain a large part of my system. The last part will explain what's still done via Ubuntu for now.

Everything should be a short introduction as there are already existing documentations and blog posts.

Benefits over other solutions

Nix works atomic. Before switching, it will build the new system first. Old systems are kept and one can switch back and forth. E.g., in case of breaking changes.

home-manager is declarative. I can declare the system I want to have and home-manager will use nix and nixpkgs to build this system. It also provides some so-called modules which allow me to configure XDG files, such as desktop files. I can put the configuration under version control and share my setup. That also allows checking out any given configuration and create the system.

Nix itself allows using existing package definitions with overlays. I can create my own overlays to adjust the software. I can compile software locally with custom patches applied. Or I can compile software against another library.

Why do I switch?

I need a package manager that allows me to patch software. Most software I use is open source. That allows me to alter software to my needs.

I also am a big fan of declarative systems. I always carried a .dotfiles repo and bash script to link the files to my system. That always felt wrong.

And I prefer easy to use systems. Apt felt way to complicated for me in order to provide software installable via apt.

Nix itself is very powerful and solves all those issues for me. It also works cross-platform allowing my colleagues to switch from homebrew to the same setup.

Furthermore there is a big community with many projects and ideas around Nix. E.g. providing a Nix file to define the dependencies for a software project. That will then be used for local development, CI and production. There are projects like composer2nix which would allow to wrap any given composer project as nix derivation for easy usage. And I'm still discovering new things every week.

nix expression language

nix is an functional programming language. One could use it for arbitrary things. nixpkgs uses nix to describe how to compile software. The configuration of home-manager itself is also written in nix. And home-manager itself is also written in nix.

I'm not going into details here. One can learn the language by having a look at source code files and by reading official manual of the language: https://nixos.org/manual/nix/stable/. Still nix is the fundamental part of everything that will come next.

nix package manager

Nix also refers to the nix package manager which can be installed cross-platform. I started by using the package manager the same way I was used to Ubuntu and its package manager apt. E.g. I installed software via nix-env -i dmenu. My first contact with nix was due to suckless' tool st and dmenu. Those are configured by modifying their config.def.h file before compilation.

That didn't seem easy with Ubuntu and apt. It indeed was easy using nix as I could extend the existing definition by adding another patch file applying my configuration.

I'm not going into details on the package manager as I switched to home-manager to manage my packages and never really got used to the manual package management. This had the same downsides as Ubuntu.

nixpkgs source of packages

nixpkgs is a repository https://github.com/NixOS/nixpkgs/ providing many definitions of package installations written in nix. There is a central binary cache which can be used to fetch pre compiled binaries from this repository instead of re-compiling on every machine. There is also an official manual https://nixos.org/manual/nixpkgs/stable/ which explains how to alter definitions. It is the default repository used by nix package manager. Other repositories / sources can be added as well, e.g. a company wide repository.

home-manager managing user

This great community project allows each user of a system do declare its system. This is done within a file ~/.config/nixpkgs/home.nix. The file extension already exposes that this file is written in nix language. My own setup is available at https://gitea.daniel-siepmann.de/danielsiepmann/nixpkgs/src/branch/main/home.nix. There you can see which programs I've installed.

Thanks to nix language this file can be split into multiple files which are then imported. Some programs are also configured. E.g. Git or Neovim are installed and configured. This is done within separated files per program which are imported at the main home.nix file.

Example Git

Git is still very simple.

The first benefit is that I can define configuration within nix and don't have to worry where Git configuration is stored and which file format is used.

Second benefit is that I can reference dependencies, e.g. the pager to use within Git. Nix will resolve those dependencies and install them alongside. An example would be:

{ pkgs }:
{
  enable = true;
  extraConfig = {
    pager = {
      log = toString pkgs.less + "/bin/less";
      diff = toString pkgs.less + "/bin/less";
    };
  };
}

That would define less to be used as pager. Nix will resolve the referenced package. That would not “install” less “globally” but make it available to Git only.

My full Git configuration is available at https://gitea.daniel-siepmann.de/danielsiepmann/nixpkgs/src/branch/main/home/programs/git.nix.

Example Neovim

The following is part of my home-manager Neovim configuration. It allows me to install any given Git Repository as a Neovim plugin without worrying about any details:

{ pkgs }:
let
  colorscheme-smyckblue = pkgs.vimUtils.buildVimPluginFrom2Nix {
    name = "colorscheme-smyckblue";
    src = pkgs.fetchgit {
      url = "https://gitea.daniel-siepmann.de/danielsiepmann/vim-colorscheme-smyckblue.git";
      rev = "v1.0.1";
      sha256 = "bBX3dzqKz6kTACfyAU4HH0UFVDYdyqLTvQdYTgWw8Jg=";
    };
  };
in {
  enable = true;
  plugins = [
    colorscheme-smyckblue
  ];
}

This code block turns the Repository into a Vim plugin which I install for Neovim.

Some plugins have dependencies, e.g. to PHP, this looks like the following:

{ pkgs }:
{
  enable = true;
  plugins = with pkgs.vimPlugins; [
    {
      plugin = phpactor;
      config = ''
        let g:phpactorPhpBin = "${pkgs.php74}/bin/php"
      '';
    }
  ];
}

I install the Vim Plugin phpactor and provide configuration. This configuration is evaluated from nix resolving the dependencies. The configuration then is written to the expected place of Neovim. That allows me to switch the necessary PHP version for this specific plugin independently of anything else.

How did I swtich?

This can be seen by taking a look at the commit history of the repository: https://gitea.daniel-siepmann.de/danielsiepmann/nixpkgs/commits/branch/main.

I started by using nix-env for a small set of tools combined with overlays. Then I've switched to home-manager and with some small extra tools that are available to my user in CLI context. Next I switched over some configuration files followed by more and more programs. Last switch was Neovim as my setup is a bit large and complex.

Still Ubuntu

I still use Ubuntu to maintain parts which are integrated and where I don't have too much knowledge about. This includes the whole desktop environment as this is bootstrapped from the system's login screen, which itself is also still maintained by Ubuntu. I just didn't invest any time yet to check whether I can install my desktop environment via home-manager and start that from Ubuntu's login screen. I don't think I can maintain the login screen from within home-manager, as this is considered system-wide? But I also encrypt the hard disc, so I could auto login and maybe remove the need for a login screen and maintain everything using home-manager in that area.

Also, drivers and stuff I never touched myself is still maintained via Ubuntu. The same goes for "system services" e.g. database MySQL, php / php-fpm and webserver Apache which I need for my daily work. It looks nice how they can be configured via NixOS, but I'm still on Ubuntu and don't think I can maintain such system services via home-manager.

Further reading