First steps with NixOS

It's time for something new

As you can guess from my website, I was using Arch Linux for quite some time and I was always extremely happy with it. However, I will start a new job in February 2022 and consequently I had to configure a new laptop. So far, so easy: I thought that just cloning my dotfiles repository should be fine to be up and running without any friction. However, this is not entirely true, since Arch is mainly driven by the imperative nature of its package manager pacman. In other words, if I run a

sudo pacman -S emacs   

GNU Emacs will be installed on my system, but this is still to be performed on any new machine I want to configure. This is the point where the strengths of NixOS and more specifically the nix-driven home-manager come into play. Using these, the configuration of the entire system is built on the basis of configuration files. If you are interested, just go ahead and read the next sections for some basic examples.

We still need to partition our block device

If we want to setup a new machine from scratch, of course we need to perform some basic steps in order to get the machine's drive ready for install. For this purpose, we can simply follow the NixOS installation guide. For encrypted file systems, also take a look at the section of LUKS-encryption. Once the formatting and partitioning is done and the brand new device is mounted on /mnt, we can create a basic configuration file using the command

nixos-generate-config --root /mnt

This will create the file /mnt/etc/nixos/configuration.nix with some pieces of explanation in its comments. Note that this file describes the system configuration and can potentially be used for setting up an entirely different machine. Therefore, this enables the reuse a configuration. However, I decided to keep it minimal here and perform the more specific parts of the configuration with home-manager. As an example, you find my configuration.nix below.

# Edit this configuration file to define what should be installed on
# your system.  Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running ‘nixos-help’).

{ config, pkgs, ... }:

{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
    ];

  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
  # Required for encrypted disc to be recognized at boot
  boot.initrd.luks.devices."enc-root" = {
    device = "/dev/disk/by-uuid/20205a25-8131-4c26-aff0-deae68562045";
    allowDiscards = true;
    preLVM = true;
  }; 

  networking.hostName = "nunc"; # Define your hostname.
  networking.networkmanager.enable = true;

  # Set your time zone.
  time.timeZone = "Europe/Berlin";

  # The global useDHCP flag is deprecated, therefore explicitly set to false here.
  # Per-interface useDHCP will be mandatory in the future, so this generated config
  # replicates the default behaviour.
  # networking.useDHCP = true;
  networking.interfaces.wlp0s20f3.useDHCP = true;

  # Enable the X11 windowing system.
  services.xserver.enable = true;

  services.xserver.windowManager.i3.enable = true;
  services.xserver.windowManager.i3.package = pkgs.i3-gaps;
  services.xserver.displayManager.lightdm.enable = true;
  services.xserver.desktopManager.xterm.enable = false;

  # Somehow lightm was not started properliy without this short timeout
  services.xserver.displayManager.job.preStart = "sleep 0.25";

  # Display settings
  services.xserver.resolutions = [
    {
      x = 1920;
      y = 1200;
    }
  ];
  services.xserver.dpi = 120;

  # Configure keymap in X11
  services.xserver.layout = "de";
  # services.xserver.xkbOptions = "eurosign:e";

  # Enable CUPS to print documents.
  services.printing.enable = true;

  # Enable sound.
  sound.enable = true;
  hardware.pulseaudio.enable = true;

  # Enable touchpad support (enabled default in most desktopManager).
  services.xserver.libinput.enable = true;

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.hic = {
    isNormalUser = true;
    shell = pkgs.zsh;
    initialPassword = "1234";
    # Enable ‘sudo’ for the user. Moreover, fusuma for the user.
    extraGroups = [ "wheel" "input" ];
  };

  # List packages installed in system profile. To search, run:
  # $ nix search wget
  environment.systemPackages = with pkgs; [
    vim 
    wget
    firefox
    git
    alacritty
  ];

  environment.sessionVariables = {
    TERMINAL = "alacritty";
  };

  # This required for configuring gtk-themes via home-manager
  programs.dconf.enable = true;

  # List services that you want to enable:

  # gvfs for easy workflows with external drives
  services.gvfs = {
    enable = true;
  };

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It‘s perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "21.11"; # Did you read the comment?

}

Home-manager goes beyond a dotfiles repository

Dotfiles are great, and storing them in a public repo is even more great. Locally, I used to store them in a bare git repository with a working tree under $HOME. Moreover, I set up a few aliases allowing me to easily run some basic git operations. However, there was one thing that bothered me: the bare git repo was not easily manageable by Magit (indeed I think it is possible with some hacks, however). Therefore, I was happy to find a new workflow with a local clone of the repo and symlinking the files into the right place (which could conceptually also be achieved with GNU Stow). Fortunately, home-manager can also do exactly this, and I favor this option over configuring everything using the options provided by home-manager, as this allows me to stay a little bit independent from home-manager. However, installing programs and configuring some services is great with home-manager, and consequently my home-manager configuration looks like this.

Conclusion

I went my first steps with NixOs the last days, and conceptually I am already kind of convinced that the effort it took is an investment into the future. Most probably, I will reuse my configuration someday and then the real benefits of a declarative system configuration will become obvious. But all in all, I also want to back the observations recently shared in this blog post. In conclusion, we most probably need to be patient and wait for a documentation getting people up and running more quickly. Another opportunity is to keep being ambitious to learn something new, and become one of the guys speaking nix fluently in the long run.