I recently got a ThinkPad W540 laptop and I'm trying out the NixOS Linux distribution:

NixOS is a GNU/Linux distribution that aims to improve the state of the art in system configuration management. In existing distributions, actions such as upgrades are dangerous: upgrading a package can cause other packages to break, upgrading an entire system is much less reliable than reinstalling from scratch, you can't safely test what the results of a configuration change will be, you cannot easily undo changes to the system, and so on.

I use the Nix package manager alongside other distributions and decided to try out the full operating system. This post outlines the steps I took to install NixOS with full disk encryption using LVM on LUKS.

Windows

The W540 comes with Windows 8.1 pre-installed and recovery partitions to enable rebuilding the system. I followed the install procedure to get Windows working and proceeding to make a recovery USB drive so I could get back to the starting state if things went wrong. Once this completed I went on with installing NixOS.

NixOS Live CD

I used the NixOS Graphical Live CD to install. I could have used the minimal CD but I went fo the graphical option to make sure the basic OS worked fine on the hardware. I installed the Live CD to a USB stick from another Linux machine using unetbootin.

To boot from this I had to change the W540 BIOS settings:

Change the USB drive in the boot sequence so it was the first boot option.

Disable Secure Boot.

Change UEFI to be UEFI/Legacy Bios from the previous UEFI only setting.

Booting from the USB drive on the W540 worked fine and got me to a login prompt. Logging in with root and no password gives a root shell. Installation can proceed from there or the GUI can be started with start display-manager .

Networking

The installation process requires a connected network. I used a wireless network. This is configured in the Live CD using wpa_supplicant. This required editing /etc/wpa_supplicant.conf to contain the settings for the network I was connecting to. For a public nework it was something like:

network={ ssid="My Network" key_mgmt=NONE }

The wpa_supplicant service needs to be restarted after this:

# systemctl restart wpa_supplicant.service

It's important to get the syntax of the wpa_supplicant.conf file correct otherwise it will fail to restart with no visible error.

Partition the disk

Partitioning is done manually using gdisk. Three partitions are needed:

A small partition to hold GPT information and provide a place for GRUB to store data. I made this 1MB in size and it must have a partition type of ef02 . This was /dev/sda1 .

. This was . An unencrypted boot partition used to start the initial boot, and load the encrypted partition. I made this 1GB in size (which is on the large side for what it needs to be) and left it at the partition type 8300 . This was /dev/sda2 .

. This was . The full disk encrypted partition. This was set to the size of the rest of the drive and partition type set to 8e00 for "Linux LVM". This was /dev/sda3 .

Create encrypted partitions

Once the disk is partitioned above we need to encrypt the main root partition and use LVM to create logical partitions within it for swap and root:

# cryptsetup luksFormat /dev/sda3 # cryptsetup luksOpen /dev/sda3 enc-pv # pvcreate /dev/mapper/enc-pv # vgcreate vg /dev/mapper/enc-pv # lvcreate -L 40G -n swap vg # lvcreate -l 111591 -n root vg

The lvcreate commands create the logical partitions. The first is a 40GB swap drive. The laptop has 32GB of memory so I set this to be enough to store all of memory when hibernating plus extra. It could be made quite a bit smaller. The second creates the root partition. I use the -l switch there to set the exact number of extents for the size. I got this number by trying a -L with a larger size than the drive and used the number in the resulting error message.

Format partitions

The unencrypted boot partition is formatted with ext2 and the root partition with ext4 :

# mkfs.ext2 -L boot /dev/sda2 # mkfs.ext4 -O dir_index -j -L root /dev/vg/root # mkswap -L swap /dev/vg/swap

These should be mounted for the install process as follows:

# mount /dev/bg/root /mnt # mkdir /mnt/boot # mount /dev/sda2 /mnt/boot # swapon /dev/vg/swap

Configure NixOS

NixOS uses a declarative language for the configuration file that is used to install and configure the operating system. An initial file ready to be edited should be created with:

$ nixos-generate-config --root /mnt

This creates the following files:

/mnt/etc/nixos/configuration.nix

/mnt/etc/nixos/hardware-configuration.nix

The latter file is rewritten everytime this command is run. The first file can be edited and is never rewritten. For the initial boot I had to make one change to hardware-configuration.nix . I commented out this line:

# services.xserver.videoDrivers = [ "nvidia" ];

I can re-add it later when configuring the X server if I want to use the nvidia driver.

The changes that need to be made to configuration.nix involve setting the GRUB partition, the Luks data and any additional packages to be installed. The Luks settings I added were:

boot.initrd.luks.devices = [ { name = "root"; device = "/dev/sda3"; preLVM = true; } ];

I changed the GRUB boot loader device to be:

boot.loader.grub.device = "/dev/sda";

To enable wireless I made sure I had:

networking.wireless.enable = true;

I added my preferred editor, vim, to the system packages:

environment.systemPackages = with pkgs; [ vim ];

Enable OpenSSH:

services.openssh.enable = true;

I've left configuring X and other things for later.

Install NixOS

To install based on the configuration made above:

# nixos-install

If that completes successfully the system can be rebooted into the newly installed NixOS:

# reboot

You'll need to enter the encryption password that was created during cryptsetup when rebooting.

Completing installation

Once rebooted re-enable the network by performing the /etc/wpa_supplicant.conf steps done during the install.

Installation of additional packages can continue following the NixOS manual. This mostly involves adding or changing settings in /etc/nixos/configuration.nix and then running:

# nixos-rebuild switch

This is outlined in Changing the Configuration in the manual.

Troubleshooting

The most common errors I made were syntax errors in wpa_supplicant.conf and configuration.nix . The other issue I had was not creating the initial GPT partition. GRUB will give an error in this case explaining the issue. You can reboot the Live USB drive at any time and mount the encrypted drives to edit files if needed. The commands to mount the drives are:

# cryptsetup luksOpen /dev/sda3 enc-pv # vgscan --mknodes # vgchange -ay # mount /dev/bg/root /mnt # mkdir /mnt/boot # mount /dev/sda2 /mnt/boot # swapon /dev/vg/swap

Tips

environment.systemPackages in /etc/configuration.nix is where you add packages that are seen by all users. When this is changed you need to run the following for it to take effect:

# nixos-rebuild switch

To find the package name to use, run something like (for vim):

$ nix-env -qaP '*'|grep vim

A user can add their own packages using:

$ nix-env -i vim

And remove with:

$ nix-env -e vim

A useful GUI for connecting to wireless networks is wpa_gui . To enable this add wpa_supplicant_gui to environment.systemPackages in /etc/nixos/configuration.nix followed by a nixos-rebuild switch . Add the following line to /etc/wpa_supplicant.conf :

ctrl_interface=/var/run/wpa_supplicant

Restart wpa_supplicant and run the gui:

$ systemctl restart wpa_supplicant.service $ sudo wpa_gui

It's possible to make custom changes to Nix packages for each user. This is controlled by adding definitions to ~/.nixpkgs/config.nix . The following config.nix will provide Firefox with the official branding:

{ packageOverrides = pkgs : with pkgs; rec { firefoxPkgs = pkgs.firefoxPkgs.override { enableOfficialBranding = true; }; }; }

Installing or re-installing for the user will use this version of Firefox: