NixOS installation guide (with encrypted root)

Preparing Installation media

The NixOS ISO used is NixOS minimal iso.

dd bs=4M if=nixos-minimal-21.05.3216.ebf419e737b-x86_64-linux.iso of=/dev/sdb

Booting the Installer

Once logged in, login as root user as most commands will require root permissions.

sudo su

Internet connection

Wireless

First identify the name of your wireless interface

iw dev
phy#0
   Interface wlp3s0
   	ifindex 2
   	wdev 0x1
   	addr 00:17:6c:11:6c:c6
   	type managed

In this example, wlp3s0 is the available wireless interface, if you are unsure, your wireless interface is likely to start with the letter "w" and unlikely to be "lo" or start with the letter "e".

Bring the interface up with:

ip link set wlp3s0 up

Scan for available networks:

iw dev wlp3s0 scan | grep SSID

Connect to a network:

wpa_supplicant -B -i wlp3s0 -c <(wpa_passphrase "ssid" "psk")

Finally, you have to your interface an IP address. This can be set manually or using the dhcp:

dhcpcd wlp3s0

If that does not work, issue the following commands:

echo 'ctrl_interface=DIR=/run/wpa_supplicant' > /etc/wpa_supplicant.conf
wpa_passphrase   >> /etc/wpa_supplicant.conf
ip link set  up # May not be needed as dhcpcd should bring it up but may be needed for wpa_supplicant.
wpa_supplicant -B -D nl80211 -c /foobar.conf -i 
dhcpcd -A 

Wiping the disk

You want to make sure that the device you’re using doesn’t contain any plaintext copies of your personal data. If the drive is new, then you can skip the rest of this section; if it’s not new, then there are two ways to handle it:

The storage device is probably /dev/sda, always double check to be sure:

lsblk

If the drive were not previously encrypted, securely wipe it with the dd command; you can either choose to fill it with zeroes or random data; I chose random data (e.g., urandom), because it’s more secure. Depending on the size of the drive, this could take a while to complete:

dd if=/dev/urandom of=/dev/sdb; sync

If the drive were previously encrypted, all you need to do is wipe the LUKS header. The size of the header depends upon the specific model of the hard drive; you can find this information by doing some research online. Refer to this article, for more information about LUKS headers. You can either fill the header with zeroes, or with random data; again, I chose random data, using urandom:

head -c 3145728 /dev/urandom > /dev/sda; sync

Partitioning

Depending on your hardware, you should follow either the DOS or (U)EFI partitioning instructions. Check if the device is UEFI or legacy BIOS:

[ -d /sys/firmware/efi ] && echo "UEFI" || echo "BIOS"
lsblk

The partitioning program used here is fdisk, however any others like cfdisk or gdisk can be used to achieve the same result.

Legacy BIOS Instructions

fdisk /dev/sdX
  1. o (dos disk label)
  2. n new
  3. p primary (4 primary in total)
  4. 1 (partition number [1/4])
  5. 2048 first sector (alignment for perfomance)
  6. +500M last sector (boot sector size)
  7. rm signature (Y), if ex. => warning of overwriting existing system, could use wipefs
  8. n new
  9. p
  10. 2
  11. default (fill up partition)
  12. default (fill up partition)
  13. w (write)

UEFI Instructions

fdisk /dev/sdX
  1. g (gpt disk label)
  2. n new
  3. 1 (partition number [1/128])
  4. 2048 first sector
  5. +500M last sector (boot sector size)
  6. t
  7. 1 (EFI System)
  8. n
  9. 2
  10. default (fill up partition)
  11. default (fill up partition)
  12. w (write)

Setup the encrypted LUKS partition and open it:

cryptsetup luksFormat /dev/sda2
cryptsetup config /dev/sda2 --label NIXOS_LUKS_PARTITION
cryptsetup luksOpen /dev/sda2 enc-pv

We create two logical volumes, a 8GB swap parition and the rest will be our root filesystem:

pvcreate /dev/mapper/enc-pv
pvdisplay
vgcreate vg /dev/mapper/enc-pv
vgdisplay
lvcreate -L 2G -n NixOS_SWAP vg
lvcreate -l '100%FREE' -n NixOS_ROOT vg
lvdisplay

Format the partitions:

mkfs.fat -F 32 /dev/sda1
fatlabel /dev/sda1 NixOS_BOOT
mkfs.ext4 -L NixOS_ROOT /dev/vg/NIXOS_ROOT
mkswap -L NixOS_SWAP /dev/vg/NIXOS_SWAP

Installing NixOS

We mount the partitions we just created under '/mnt' so we can install NixOS on them.

mount /dev/disk/by-label/NixOS_ROOT /mnt
mkdir /mnt/boot
mount /dev/disk/by-label/NixOS_BOOT /mnt/boot/
swapon /dev/vg/NixOS_SWAP

WPA supplicant configuration to be able to use WIFI:

cat > /etc/wpa_supplicant.conf
network = {
  ssid="****"
  psk="****"
}
^D
systemctl start wpa_supplicant

Generate NixOS configuration and modify it to your liking.

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

The following config is just a start to give an idea of how to configure.

{ config, lib, pkgs, ... }: {
  imports = [ ./hardware-configuration.nix ];

  boot = {
    loader = {
      efi = {
        canTouchEfiVaribles = true;
        efiSysMountPoint    = "/boot/efi";
      };
      grub = {
        device                = "nodev"
        efiInstallAsRemovable = true;
        efiSupport            = true;
        enable                = true;
        enableCryptodisk      = true;
      };
    };
    initrd.luks.devices = {
      "root" = {
        allowDiscards = true;
        device        = "/dev/disk/by-label/NIXOS_LUKS_PARTITION";
        preLVM        = true;
      }
    };
  };

  networking = {
    hostName                 = "laptop";
    interfaces.wlp3s0.useDHCP = true;
    useDHCP                  = false;
    wireless                 = {
      enable     = true;
      interfaces = ["wlp3s0"];
      networks   = {
        SSID??? = {
          pskRaw = "???????";
        };
      };
      userControlled.enable = true;
    };
  };

  time.timeZone = "Europe/London";

  i18n.defaultLocale = "en_GB.UTF-8";
  console = {
    font = "Lat2-Terminus16";
    keyMap = "uk";
  };

  services = {
    displayManager.defaultSession = "none+awesome";
    libinput = {
      enable = true;
      touchpad.disableWhileTyping = true;
    };
    pipewire = {
      enable = true;
      pulse.enable = true;
    };
    xserver = {
      enable = true;
      xkb.layout = "gb";
      windowManager = {
        awesome = {
          enable = true;
          luaModules = with pkgs.luaPackages; [ luarocks lgi vicious ];
        };
      };
    };
  };

  users.users.eric = {
    isNormalUser = true;
    home = "/home/eric";
    createHome = true;
    extraGroups = [ "wheel" ];
    packages = with pkgs; [
      firefox
      tree
    ];
  };

  environment.systemPackages = with pkgs; [
    vim
    git
    brave
    keepass
    alacritty
    ntfs3g
  ];

  system.stateVersion = "24.05";
}

Example hardware-configuration.nix:

{ config, lib, pkgs, modulesPath, ... }: {

  imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];

  boot = {
    initrd = {
      availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "usb_storage" "sd_mod" ];
      kernelModules = [ "dm-snapshot" ];
      luks.devices."root" = {
        allowDiscards = true;
        device = "/dev/disk/by-label/NIXOS_LUKS_PARTITION";
        preLVM = true;
      };
    };
    kernelModules = [ "kvm-intel" ];
    extraModulePackages = [ ];
  };

  fileSystems = {
    "/" = {
      device = "/dev/disk/by-label/NixOS_ROOT";
      fsType = "ext4";
      options = [ "noatime" "nodiratime" "discard" ];
    };
    "/boot" = {
      device = "/dev/disk/by-label/NixOS_BOOT";
      fsType = "vfat";
      options = [ "fmask=0022" "dmask=0022" ];
    };
  };

  swapDevices = [ { device = "/dev/disk/by-label/NixOS_SWAP"; } ];

  networking.useDHCP = lib.mkDefault true;

  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

Once configuration is complete:

Install NixOS and rebbot.

nixos-install
reboot

Troubleshooting

If for whatever reason the system doesn't boot, we can go back to the installation environment by booting from the installation media and remounting all partitions:

cryptsetup luksOpen /dev/sda2 enc-pv
lvchange -a y /dev/vg/NixOS_SWAP
lvchange -a y /dev/vg/NixOS_ROOT
mount /dev/vg/NixOS_ROOT /mnt
mount /dev/sda1 /mnt/boot
swapon /dev/vg/swap
cp /mnt/etc/wpa_supplicant.conf /etc
systemctl start wpa_supplicant