JeffTP

Squandering a perfectly good opportunity to shut up and listen.

Partitioning NixOS with Disko

Published: 2024-03-27 • Reading time: 6 min

#linux #nixos #disko #sysadmin

About a month ago I decided to spend my free time getting more familiar with NixOS. In order to install NixOS, I needed to figure out what Linux Partitioning scheme I would use. Rather than manually partitioning, I decided I would dive straight into learning Disko the declarative disk partitioning system.

Diving into Disko ended up being quite an adventure, as I suspect much of my exploration of NixOS will be. I'm not content to simple type magic incantations into a configuration file, but rather I want to know what each magic incantation does. Let me share with you my journey, what I've learned so far, and my Disko configuration.

Exploring NixOS seems to be deeply rooted in "mess around and find out." There's a common thread across the Internet that NixOS documentation is lacking, especially when compared to Arch Linux which mostly has outstanding documentation. Fortunately, messing around and finding out seems to be a pretty normal pattern for me to learn new things and something I mostly enjoy.

Still, I can't decide if the time I'm investing in trying to figure out how Disko works for partitioning is worth the investment. I could have just used parted, pvcreate, vgcreate, lvcreate, mkfs and been done already.

Building my Disko Configuration

In my initial exploration of the example configurations in the Disko git repository I can't shake the feeling that there's quite a bit of TIMTOWTDI (There Is More Than One Way To Do It, the reason why Perl scripts are so easy to write and so difficult to read) with Disko configuration. Is there an advantage to using start and end when specifying partitions, or is size good enough. I'm not sure I care about the start and end assuming Disko is smart enough to align the partitions.

I ended up diving into the Disko source code to get a better feel for the differences between start, end, and size. Here I discovered my first surprise: Disko is written in the Nix language. It felt like a moment where I discovered it's "turtles all the way down."

As I dug through the source I ran into a valuable comment from /lib/types/table.nix: The legacy table is outdated and should not be used. We recommend using the gpt type instead. This helped invalidate various Disko configurations I was looking at that were still using table.

As I continued to scan the source code I was particularly interested in how Disko would handle LVM configuration, as I had already decided that I would be using LVM as part of my partition configuration. Disko has a type for lvm_pv and lvm_vg but not lvm_lv. Within lvm_vg there's an lvs item where you specify your logical volumes.

As a reminder, my partition scheme:

  • /dev/sda1 -- /boot, 1 GB, FAT32

  • /dev/sda2 -- (LVM vg1), the rest, LVM

    • /dev/vg1/swap -- (swap), 8G, swap
    • /dev/vg1/nix -- /nix, 50G, ext4
    • /dev/vg1/root -- /, the rest, ext4

    Based on the above, I was able to build the Disko configuration below:

{
  disko.devices = {
    disk.sda = {
      type = "disk";
      device = "/dev/sda";
      content = {
        type = "gpt"; # Initialize the disk with a GPT partition table
        partitions = {
          ESP = { # Setup the EFI System Partition
            type = "EF00"; # Set the partition type
            size = "1000M"; # Make the partition a gig
            content = {
              type = "filesystem";
              format = "vfat"; # Format it as a FAT32 filesystem
              mountpoint = "/boot"; # Mount it to /boot
            };
          };
          primary = { # Setup the LVM partition
            size = "100%"; # Fill up the rest of the drive with it
            content = {
              type = "lvm_pv"; # pvcreate
              vg = "vg1";
            };
          };
        };
      };
    };
    lvm_vg = { # vgcreate
      vg1 = { # /dev/vg1
        type = "lvm_vg";
        lvs = { # lvcreate
          swap = { # Logical Volume = "swap", /dev/vg1/swap
            size = "8G";
            content = {
              type = "swap";
            };
          };
          nix = { # Logical Volume = "nix", /dev/vg1/nix
            size = "50G";
            content = {
              type = "filesystem";
              format = "ext4";
              mountpoint = "/nix";
              mountOptions = [
                "noatime" # Reduce writes--we don't care about access times
              ];
            };
          };
          root = { # Logical Volume = "root", /dev/vg1/root
            size = "100%FREE"; # Use the remaining space in the Volume Group
            content = {
              type = "filesystem";
              format = "ext4";
              mountpoint = "/";
              mountOptions = [
                "defaults"
              ];
            };
          };
        };
      };
    };
  };
}

How to run Disko

The configuration file alone isn't enough to get the partitions created. You've got to actually run Disko.

  • First, you need to boot up the NixOS installer.
  • I stored my disko-config.nix as a gist on GitHub. Seemed like as good as place as any to host it.
  • I created a TinyURL shortcut for that link because there's no way I'm going to attempt to type that whole URL.
  • Download the file using curl. Switch -L will follow redirects, -o specifies the file to store the output. My URL is a direct link raw disko-config.nix from GitHub.
    • curl -L <url> -o /tmp/disko-config.nix
  • Run disko:
    • sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko -- --mode disko /tmp/disko-config.nix
    • There's a couple of modes
      • --mode disko will unmount and destroy all filesystems on the disks we want to format before creating and mounting our filesystems specified in the config file
      • --mode format will create partition tables, logical volumes, and filesystems
      • --mode mount will mount the partition at the specified root-mountpoint
      • I'm not sure when the format and mount options are useful. I didn't try either.
  • Complete the NixOS install
    • nixos-generate-config --no-filesystems --root /mnt
      • The --no-filesystems switch is used because we're defining the filesystems in disko-config.nix.
    • cp /tmp/disko-config.nix /mnt/etc/nixos
      • Copy our disko config to the future /etc/nixos directory.
    • Edit /mnt/etc/nixos/configuration.nix to add the disko NixOS module and our disko-config.nix file to the imports section:
imports =
  [
    ./hardware-configuration.nix
    "${builtins.fetchTarball} "https://github.com/nix-community/disko/archive/master.tar.gz"}/module.nix"
    ./disko-config.nix
  ];
  • And finally:
    • Edit additional options in /mnt/etc/nixos/configuration.nix:
      • networking.hostName
      • networking.networkmanager.enable
      • time.timeZone
      • environment.systemPackages Don't forget to install an editor!
    • Execute the install
      • nixos-install
      • reboot

This is a fine first step into NixOS and Disko but there's more to learn about getting a system off the ground with Flakes. But that's a story for another day.