A Dev's Journey - Part 01 - get NixOS; prepare file system
First I need to bootstrap my setup. I use my existing ubuntu installation to download and write the NixOS installation image to a thumb drive. This will be my actual entry point to all further preparations and installation.
Over in the download section at nixos.org two options are presented. A Graphical ISO image and a Minimal ISO image.
The graphical images come with either Gnome or Plasma desktop and will provide a graphical installation wizard. This live image allows to try out NixOS without installing it. If connected to the internet one can still select another desktop environment during the installation process. Unfortunately, not hyprland. But is still a good starting point, as it generates a decent default configuration to build upon.
But I want to learn how to prepare and set up everything from the ground up. So I go for the minimal image, and therefore a console based installation experience.
Note this URL is valid as of the day I’m writing this. I suggest you check out the most recent and recommended link on nixos.org.
> curl --create-dirs --output-dir /tmp/nixos_image -O https://releases.nixos.org/nixos/23.05/nixos-23.05.2385.48e82fe1b1c/nixos-minimal-23.05.2385.48e82fe1b1c-x86_64-linux.iso > curl --create-dirs --output-dir /tmp/nixos_image -O https://releases.nixos.org/nixos/23.05/nixos-23.05.2385.48e82fe1b1c/nixos-minimal-23.05.2385.48e82fe1b1c-x86_64-linux.iso.sha25 > (cd /tmp/nixos_image && sha256sum --check nixos-minimal-23.05.2385.48e82fe1b1c-x86_64-linux.iso.sha256)
sha256sum check reports that the downloaded image is OK.
- Acquire NixOS ISO image. Check.
Time to plug and locate the thumb drive.
Here are some tools that help to identify a plugged thumb drive:
The easiest way to identify a removable drive, is to call one of the commands above, before plugging it in. Then call the command again with the device plugged in and watch for changes.
fdisk and check the
Disk model field for the name of my thumb drive.
My build in drive is from Samsung and ~480GB in size.
My thumb drive in an Intenso Premium Line and ~ 32GB in size.
> sudo fdisk -l Disk /dev/sda: 476,94 GiB, 512110190592 bytes, 1000215216 sectors Disk model: SAMSUNG MZ7LN512 … Disk /dev/sdb: 30,27 GiB, 32497729536 bytes, 63472128 sectors Disk model: Premium Line …
One quick look at the output and one can tell that my thumb drive has been assigned to block device
Last thing left is to write the ISO to the drive.
For this I will use the good old
dd command like this:
> sudo dd if=/tmp/nixos_image/nixos-minimal-23.05.2385.48e82fe1b1c-x86_64-linux.iso \ of=/dev/sdb bs=32M status=progress conv=sync
The installation medium is complete and can be unplugged. In theory, I should be able to proceed with just the thumb drive, and without the help of any other devices. But I leave the lid of this laptop open, just in case I need to search the internet (which I probably will). Time to switch to the target device (in my case: another laptop).
File System and Encryption
Here is a quick recap of my plan for the hard drive:
+-----------+----------+----------+ | | | | | [/boot] | [/ ] | | | vfat | ext4 | swap | | 1GB | ~967GB | 32GB | | | | | | |---------------------| | | L U K S 2 | +-----------+----------+----------+
What is going on here?
All data but
/boot are encrypted by LUKS2.
Even the swap, as it may contain open and loaded files while hibernating.
There is one misconception about my plan.
As shown above, I’ve thought, that one LUKS2 partition drive may contain several sub partitions.
Of course this is not the case and would require LVM or btrfs to do so.
But still, I stick to my idea to use three partitions of whom
swap are secured with LUKS2.
So I fixed the outline to look like this:
+-----------+----------+----------+ | | | | | [/boot] | [/ ] | | | vfat | ext4 | swap | | 1GB | ~967GB | 32GB | | | | | | |---------------------| | | LUKS2 | LUSK2 | +-----------+----------+----------+
Note: My current hard disk is smaller than the final one. In this example I’m using a 256 GB drive.
Boot from USB
Before powering my laptop, I plug the thumb drive with the NixOS image into a free USB slot.
Every laptop or PC has some kind of mechanic to interrupt the normal star procedure, and allow to boot from a different device.
In my case it is
F12 which brings up a boot selection menu from where I can choose the prepared thump drive.
Next, the bootloader menu shows up with the default NixOS installer preselected. I confirm the selection and boot into the live version of NixOS.
First thing on my agenda, after hitting the command line prompt of the terminal, is to select my go to keyboard layout: bone.
> sudo loadkeys de bone
I was surprised how easy it was to get bone up and running, with all layer behaving as expected.
The current go to standard for partition tables is the GUID Partition Table (GPT).
It will contain the three partitions I need.
A rather small EFI partition for
/boot, a swap partition matching the RAM size (for hibernation, or suspend to RAM),
and a large one serving as the system root
For this purpose I will use
As before, I determined the path to the desired hard drive
sudo fdisk /dev/sda
Most of the fancy values, like start and end sectors, are calculated by fdisk. To keep it simple I will focus on my custom entries here. For all skipped values, assume the suggestion accepted.
G-> 1024 * 1024 * 1024
GB-> 1000 * 1000 * 1000
- create a new empty GPT ->
- create first new partition ->
- last sector = first sector + 1 GiB ->
- change the partition type to EFI System ->
- last sector = first sector + 1 GiB ->
- create second new partition ->
- last sector = remaining space -32GiB ->
- last sector = remaining space -32GiB ->
- create third new partition ->
- take all the remaining 32GiB
- change the partition type to Linux swap ->
- write partition table to disk ->
I find it really convenient , that fdisk allows relative sector position statements.
+1G will add 1 GiB to the start sector position,
-32G will subtract 32 GiB from the last sector of the remaining space.
This saves the hassle of calculating the positions on my own.
But just for the sake of completeness:
# Sector size is 512 Byte sector_size = 512 # 1 GiB is 1073741824 Bytes gib = 1024*1024*1024 # 1 GiB is 2097152 sectors in size sectors_in_gib = int(gib / sectors_size) # Start sector is at 2048 start_sector = 2048 # The start sector already belongs into the range as it is our first sector. # Because we don't start at 2049 but at 2048 we can subtract 1. end_sector = int(start_sector + sectors_in_gib) - 1 2099199
+1G for the end sector of the first partition would be the same as
The following commands may also come in handy during the process:
- print the current state of the table draft ->
- quit without applying any changes ->
- show help/manual ->
The table looks something like this now:
Device Start End Sectors Size Type /dev/sda1 2048 2099199 2097152 1G EFI System /dev/sda2 2099200 421279835 419188736 199.9G Linux filesystem /dev/sda3 421278936 488396799 67108864 32G Linux swap
Formatting and Encryption
Now that the partition table is set up, let’s commence with the establishment of the file system.
boot / sda1
A short glimpse into the UEFI specs and one can tell,
that we should go with
FAT32 for the
/boot partition. I like to keep things simple, so let’s finish this one quickly.
sudo mkfs.fat -F32 -n "EFI BOOT" /dev/sda1
root / sda2
Finally we’ve come to the exiting part of this chapter.
The order in which encryption is applied and file system is set up is as follows.
First the partition is encrypted using LUKS2 with the help of the
Then the LUKS2 layer is unlocked and the
mkfs.ext4 command is used to emplace the file system in there.
cryptsetup luksFormat, the user is prompted to confirm the irrevocable overwrite.
It should be OK as I do not expect to have any accessible data at this point anyway.
One important parameter I want to mention.
-y will verify the passphrase by asking for it twice.
IMHO this should be the default behavior, but who am I to judge.
I assume the remaining parameters are self explanatory.
sudo cryptsetup luksFormat -y --type luks2 /dev/sda2
LUKS2 encrypts on block device level.
Which means, in order to facilitate file writing a file system is required.
Therefore access to the underlying block device is necessary.
cryptsetup luksOpen allows access to a LUKS2 secured block device by a given label.
This will map the block device to
sudo cryptsetup luksOpen /dev/sda2 root
Afterwards the file system can be created as usual, barring the extraordinary path.
sudo mkfs.ext4 -L "root" /dev/mapper/root
swap / sda3
Let’s walk through the future boot process and assume wakeup from hibernation, or suspend-to-RAM.
The bootloader at
/boot is not a problem, as this partition is not encrypted.
Now we need to unlock the root file system
/, so we type in a password.
Then, we still need to unlock the
Remembering and entering a second password is more than inconvenient.
Fortunately we just unlocked an encrypted file system, which in turn may contain a secret to unlock further partitions. LUKS2 provides a variety of different key types. One of which is a keyfile made of arbitrary binary data.
Let’s mount the previously created file system and place a keyfile there.
sudo mount /dev/mapper/root /mnt sudo dd bs=2048 count=1 if=/dev/urandom of=/mnt/keyfile iflag=fullblock
The following steps are similar to those in
root / sda2,
except the keyfile is used instead of a passphrase.
sudo cryptsetup luksFormat --type luks2 --key-file /mnt/keyfile /dev/sda3 sudo cryptsetup luksOpen --keyfile /mnt/keyfile /dev/sda3 swap sudo mkswap -L "Linux swap" /dev/mapper/swap
The bootloader can be configured to automatically unlock the swap partition using the keyfile, once the root file system gets unlocked.
In the end, I got what I was planing for. Encrypted root file system and swap. It already took me a considerable amount of time and effort to write this chapter. After a short peek into the YubiKey topic, I decided to defer it to a future chapter.
This is it for now. Here is a breakdown of my hard drive:
lsblk -o name,size,type,fstype,label NAME SIZE TYPE FSTYPE LABEL sda 232.9G disk +- sda1 1G part vfat EFI BOOT +- sda2 199.9G part crypto_LUKS |+-- root 199.9G crypt ext4 root +- sda3 32G part crypto_LUKS +-- swap 32G crypt swap Linux swap
For further investigation of the LUKS2 devices the
cryptsetup luksDumpcommand will come in handy.
See you next time!