May 19, 2022

ptemplates

Born to play

Reliable and reproducible Linux installation with NixOS

When using an operating system, upgrading packages or installing new ones are common tasks that introduce the risk of affecting the stability of the system. NixOS is a Linux distribution that ensures the reliability of the operating system and allows easy reproducibility of the system current and previous states.

This article follows our Nix introduction and deploy NixOS on your machine. It explains how NixOS works, how to obtain and install it, and how Nix ensure reliability. Your machine will boot with a working NixOS system and you will gain knowledge on how NixOS, the Nix package manager, and Home Manager interact together.

What is NixOS?

NixOS is a Linux distribution. It is built on top of Nix, a functional package manager, which language is inspired by functional programming. NixOS takes the power of the Nix package manager and applies it to the entire system. That means, among other things, it is easy to roll back the whole configuration of the system to an earlier state. Complementary to the system managed by NixOS, Home Manager manages a user environment.

Why NixOS?

NixOS applied Nix fundamentals to the entire system. That leads to:

  • System reproducibility: given a specification of a system (in a NixOS configuration file), it is possible to reproduce the entire system (modulo mutable state, such as the contents of databases for example) to another machine.
  • Atomic upgrades and rollbacks: changes made at the system level or the package level are always revertible.
  • Dependencies management: NixOS uses the Nix package management. Nix ensures that the dependencies declaration is complete when installing a package. Because Nix stores packages in isolation between them, it is possible to have different versions of the same package installed. So different packages can then use different versions of the same dependency without a problem. That management of dependencies does not lead to apps with huge sizes as it is the case using flatpack.

Installation of NixOS

The machine used during the installation is a Dell Precision 5520 laptop with 1TB SSD and 32GB RAM. The instructions shall apply to any machine, whether it is a development computer, a laptop or a virtual machine.

Obtaining NixOS ISO

NixOS ISO image can be downloaded from the NixOS download page. The ISO image file is available in two options:

  • The graphical ISO image (the easiest choice): with this option, the installation is simpler as it has the graphical interface and the networking, ready to use, needed for the installation.
  • The Minimal ISO image (non-graphical): this is the minimalistic ISO image in terms of content. The advantage is the lower size of the ISO image. But the drawback is that there is more to prepare before the installation. Here is how to prepare the networking in the installer.

Networking must be configured before the installation to download the requested dependencies.

My installation uses the graphical ISO image with the Gnome desktop environment. The size is relatively small, about 2GB.

Boot the installer

The .iso disk image is used to create a bootable USB drive. The NixOS official documentation covers the process. Follow the Ubuntu documentation for a more user-friendly approach using balenaEtcher.

Once completed, restart your targeted machine and boot from the USB drive. The screen presents a graphical interface from where NixOS can be configured and installed. A first screen proposes several variations of the installer, select the first proposition. A few seconds later, Gnome is up and running from the USB system. Open a new terminal.

Partitioning

NixOS installer doesn’t do any partitioning or formating. It is the user responsibility. To perform this operation, it is necessary to know the hard drive’s name. The command below helps to know the hard drive’s name:

In our case of installation, the hard drive’s name was /dev/nvme0n1. But depending on the disk type (SATA, SSD, NVMe, …), it is possible to have alternative values such as /dev/sda. For the next of this article, commands are based on the drive name /dev/nvme0n1.

Once the drive’s name is known, the next step is partitioning. In our case, a single partition is entirely dedicated to the operating system. Hibernation across reboot persists the system state on disk into swap space. Thus, it requires the creation of a swap partition. It is not recommended to enable hibernation on system with large RAM resources such as a server. If you choose to enable hibernation, set the swap size to equal 1.5 times the RAM size.

The UEFI partition scheme is used as the booting method. The swap partition uses 50 GiB. The MBR partition scheme is also presented for illustration purpose.

From the terminal, log in as root with sudo su -. Both fdisk and Parted are valid tools to partition the drive.

Formatting

Quick recap, in our installation, NixOS targets the /dev/nvme0n1 disk. The /dev/nvme0n1p2 partition is the root of the Linux system. The /dev/nvme0n1p3 partition is the swap drive.

In this step, the objectives are to format the partitions, activate the swap partition and mount the target file system on which NixOS is going to be installed. Here are commands for UEFI and MBR (Legacy boot) booting methods:

  • UEFI case

    
    mkfs.ext4 -L nixos /dev/nvme0n1p2
    mkswap -L swap /dev/nvme0n1p3
    mkfs.fat -F 32 -n boot /dev/nvme0n1p1
    
    mount /dev/disk/by-label/nixos /mnt
    swapon /dev/nvme0n1p3
    mkdir -p /mnt/boot
    mount /dev/disk/by-label/boot /mnt/boot
  • MBR case

    mkfs.ext4 -L nixos /dev/nvme0n1p1
    mkswap -L swap /dev/nvme0n1p2
    mount /dev/disk/by-label/nixos /mnt
    swapon /dev/nvme0n1p2
    nixos-generate-config --root /mnt

NixOS configuration

Installation is done via the NixOS configuration file in /mnt/etc/nixos/configuration.nix. The commands to generate the configuration file and open it for edition:

nixos-generate-config --root /mnt
nano /mnt/etc/nixos/configuration.nix

In the NixOS philosophy, the NixOS configuration file reflect the entire system. It includes the packages to install, the service to run, the settings to apply, the network configuration, and potentially a lot more. To make this introduction easier to grasp, we will start with a minimal configuration and then complete it once the system reboot. In the future, you are encourage to commit this configuration. This way, on a new machine installation, you have the possibilty to clone your configuration and re-apply it, or a subset of it, to a new targeted environnment.

A minimal NixOS configuration file targetting the Gnome desktop environment and the UEFI booting method is shown below. If you wish to start with a more complete system, you can enrich the configuration with your own properties or use the more exhaustive configuration file proposed at the end of this article.

 config, pkgs, ... :


  imports =
    [ 
      ./hardware-configuration.nix
    ];

  
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
  
  
  
  
  networking.useDHCP = false;
  networking.interfaces.wlp2s0.useDHCP = true;
  networking.networkmanager.enable = true;

  
  services.xserver.enable = true;

  
  services.xserver.displayManager.gdm.enable = true;
  services.xserver.desktopManager.gnome.enable = true;
  
  
  services.xserver.layout = "fr";

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

  
  services.xserver.libinput.enable = true;

  
  
  users.users.florent = 
    isNormalUser = true;
    initialPassword = "secret";  
    extraGroups = [ "wheel" ]; 
  ;

  
  
  environment.systemPackages = with pkgs; [
    vim
  ];

  
  services.openssh.enable = true;

  
  
  
  
  
  
  system.stateVersion = "21.05"; 

Installation

This is the last step before rebooting into the system. Internet connection is required to download dependencies. The installation reflects the content of the configuration created previously. The command to launch the installation is:

A root password is requested. Once completed, the system is ready on restart.

Changing the NixOS configuration

When the system is up, the system will evolve with your needs. New tools are installed, services are started and configuration are updated. This is part the system lifecycle whether the system targets a development machine or a production server.

The NixOS configuration file reflects the configuration at the system level, affecting all users created on the machine. In addition, Home manager works at the user level. It installs software and configuration for a specific user.

Adding the package curl at system level is done with the configuration below:

 config, pkgs, ... :


  ...
  environment.systemPackages = with pkgs; [
    vim 
    curl
  ];
  ...

Any change in the NixOS configuration file leads to a new booting configuration. The commands below build the configuration declared in the NixOS configuration file and make it the default configuration for booting:

At any application of the command nixos-rebuild switch, a new boot configuration is available at the start of the operating system. Here is an example screen on reboot:


Command to list the booting configurations on NixOS:

sudo nix-env -p /nix/var/nix/profiles/system --list-generations

Our Nix introduction lists the most common commands.

What is Home Manager?

Home Manager is a tool to manage a user environment using Nix package manager. As such, it completes NixOS. There are two ways to use Home Manager:

  • Using the standalone home-manager tool

    It allows managing the home directory of a user independently of the system as a whole. There are two configuration files to maintain: one file for the configuration at system-level (/etc/nixos/configuration.nix) and one file for the configuration at user-level (~/config/nixpkgs/home.nix). The former requires root privileges while the later is executed by the user without sudoers permissions.

  • As a module within a NixOS system configuration

    It allows to manage system-level configuration and user-level configuration within a single file (/etc/nixos/configuration.nix). Root level privileges are required to apply Home Manager updates.

I found it easier to maintain my system configuration in one single file. After all, I am the only user of my development machine. We cover below the installation of Home Manager as a module within the NixOS system configuration.

Home Manager as a module of NixOS

Installation of Home manager as NixOS module requires root-level privileges. From the terminal, log in as root with sudo su -. Then follow the steps below to set up Home Manager:

  • Use the commands below to add the Home Manager channel:

    nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager
    
    nix-channel --update
  • Add in the imports section of the NixOS configuration file. A new NixOS option called home-manager.users is now availble.

Given the example of NixOS configuration file in section Generation and configuration of the NixOS configuration file, adding Home Manager module to install the python3 package and to configure the dotfile .git for a user named florent gives:

 config, pkgs, ... :


  imports =
    [
      ./hardware-configuration.nix
            <home-manager/nixos>    ];

  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
  networking.useDHCP = false;
  networking.interfaces.wlp2s0.useDHCP = true;
  networking.networkmanager.enable = true;
  services.xserver.enable = true;
  services.xserver.displayManager.gdm.enable = true;
  services.xserver.desktopManager.gnome.enable = true;
  services.xserver.layout = "fr";
  sound.enable = true;
  hardware.pulseaudio.enable = true;
  services.xserver.libinput.enable = true;
  users.users.florent = 
    isNormalUser = true;
    initialPassword = "titi"  
    extraGroups = [ "wheel" ]; 
  ;
    home-manager.users.florent =  pkgs, ...:         home.packages = [       pkgs.python3    ];        programs.git =       enable = true;      userName = "Florent";      userEmail = "[email protected]";    ;  ;
  environment.systemPackages = with pkgs; [
    vim 
    curl
  ];
  services.openssh.enable = true;
  system.stateVersion = "21.05"; 

Like previously, use nixos-rebuild switch to apply changes.

Conclusion

NixOS applies Nix fundamentals to the entire system for a holistic Nix experience. Nix simplify the process of saving, sharing or replicating the configuration of machines. Applied to the whole system, it creates a flexible, reliable and reproductible Linux distribution. One can easily imagine the appeal of these benefits applied to CI/CD environments and distributed clusters.