Diving Into NixOS (Part 1.5): Swap Files, And Other Tidbits

A brief digression

As mentioned in Part 1, my swap partition may have been a little excessive. In this post, I intend to demonstrate how to go about reducing the swap size in favor of a swap file that is created only when it is needed. The Arch Wiki has a healthy amount of information on swap for those that are interested. Broadly speaking, the motivation behind having swap space is the following (snippet from the Arch Wiki itself):

[Enabling swap] avoids out of memory conditions, where the Linux kernel OOM killer mechanism will automatically attempt to free up memory by killing processes.

Swap files on NixOS

Configuring a swap file on NixOS is trivial. A quick search through the NixOS options shows the swapDevices.*.size option.

If you previously created a swap partition and ran nixos-generate-config in your initial install of NixOS - as most would have, you will find that the swapDevices option has already been configured in the file at /etc/nixos/hardware-configuration.nix. Here's a snapshot mine at the time of writing.

# Do not modify this file!  It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations.  Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:

  imports =
    [ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>

  boot.initrd.availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
  boot.kernelModules = [ "kvm-intel" ];
  boot.extraModulePackages = [ ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/b90bca12-d251-46e0-a407-f0b1ec87970b";
      fsType = "ext4";

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/50C8-A123";
      fsType = "vfat";

  swapDevices =
  [ { device = "/dev/disk/by-uuid/916812ug-56b8-417b-bdf1-1bdee27d4499"; }

  nix.maxJobs = lib.mkDefault 8;
  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";

To add a swap file, it is as simple as adding an additional set to the list and re-building.

  swapDevices = [
      device = "/dev/disk/by-uuid/916812ug-56b8-417b-bdf1-1bdee27d4499";
      priority = 100;
      size = null;
      device = "/swapfile";
      priority = 0;
      size = 2048;

Note that the swap partition is given higher priority. The general consensus seemed to be that the partition will have better performance than the swap file.

Re-sizing the swap partition

With the easy part over, we can move on to the second objective of re-sizing our partitions. This isn't actually too difficult either. It can be broken down into a few steps: update the partition table; resize the adjacent drive; then update (once again) /etc/nixos/hardware-configuration.nix.

Start by making sure the swap device is de-activated and the root partition is unmounted. To do this, I simply booted into the NixOS live image I had prepared from Part 1 and manipulated the partitions from there. The following may come in handy.

swapoff /dev/disk/by-label/swap
umount /dev/disk/by-label/nixos

To re-size the partitions I opt for gdisk.

# gdisk /dev/nvme0n1
GPT fdisk (gdisk) version 1.0.3

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help):

Before re-sizing, the table shows my swap with a size of ~2GB.

Command (? for help): p
Disk /dev/nvme0n1: 500118192 sectors, 238.5 GiB
Model: PC401 NVMe SK hynix 256GB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 399YGH4A-4E93-4AE1-A42A-A63649JREDEE
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 500118158
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1333247   650.0 MiB   EF00  EFI system partition
   2         1333248         1595391   128.0 MiB   0C01  Microsoft reserved ...
   3         1595392       206395391   97.7 GiB    0700  Basic data partition
   4       206395392       495802367   138.0 GiB   8304  Linux x86-64 root (/)
   5       495802368       500118158   2.1 GiB     8200  Linux swap

Re-size the swap partition by first deleting both the root and the swap partitions. This is followed by re-creating the deleted entries with the new desired sizes. Take care to ensure the start sector for your root partition is the same as it previously was otherwise you may bork your file system. There are ways to shift partitions (dd can be used to store/load the contents of a file system), but perhaps that is for another time. Remember to commit to the table with w.

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1333247   650.0 MiB   EF00  EFI system partition
   2         1333248         1595391   128.0 MiB   0C01  Microsoft reserved ...
   3         1595392       206395391   97.7 GiB    0700  Basic data partition
   4       206395392       498948095   139.5 GiB   8304  Linux x86-64 root (/)
   5       498948096       500118158   571.3 MiB   8200  Linux swap

With the partitions re-sized, check the file system is consistent before re-sizing it to match the new partition sizes.

# e2fsck -f /dev/disk/by-label/nixos
# resize2fs /dev/disk/by-label/nixos

Finally, re-create the swap device with mkswap and update the UUID under swapDevices in your /etc/nixos/hardware-configuration.nix alongside the swap file entry.

I found this little exercise quite fun and educational, but I still have much to learn in way of partition tables, file systems, etc. Hopefully, with this, you not only know how to configure swap devices in NixOS but can also manipulate partitions/file systems to a certain degree. Now, onto Part 2.

Helpful links