Installing Linux Mint with LVM and LUKS
So, I got a new work laptop. I'm one of a few people in our company who prefers to set up the laptop myself. And I'm a Linux kind of person, so of course it will be running some flavour of Linux. LinuxMint, to be exact. Why? Because it works OOTB and I get the best out of the Linux world w/o having to spend days tinkering the environment to my liking. Although I'm considering NixOS too... But that's for another time. Now, over the years of owning a Linux laptop (actually, laptop*S* -- my personal ones are also Linux-driven), I've noticed several patterns making my life easier. One of such is using LVM for disk partitioning. 2 VGs are usually enough: system and user. System hosts root and swap (if needed), and user is for.. you know.. all the user stuff, starting with /home. One of the reasons I really like LVM is that you can easily extend your filesystems if you need more space. And here's the trick: you should NOT assign all your free space from the beginning to leverage this LVM's feature. Here's how I normally do: partition the disk into EFI /boot LVM shards -- 4-5x unformatted partitions of 50GB each, and the rest -- ~20GB each pvcreate all the shards vgcreate both system and user groups with ~40-50GB each (initially) lvcreate both root and home volumes, create filesystems install OS there and use it normally vgextend + lvextend the filesystem that's running out of space. IDK which one it will be in advance, so it's really nice to have the flexibility to do this when I need it, w/o planning in advance. Now here's the rub. It all works OOTB with my personal laptop, but for a work laptop, I must use encrypted storage. And in order to do anything with an encrypted volume, I first have to unlock (open) it. And this post is a list of what I had to do to make LUKS+LVM work together. Partitioning Let's assume we are on an empty device. At first I wasn't, but I nuked all MS Windows-related filesystems right away and started with a tabula rasa. Using gparted (or parted if it's more convenient), create 3 partitions: ~256MB in size -- UEFI ~10GB in size -- /boot (ext4) remaining free space -- LUKS (unformatted) Suppose, the LUKS partition is /dev/nvme0n0p3 and /boot is /dev/nvme0n0p2 LUKS setup Create LUKS container First, we need to create a LUKS container, and then we'll be able to create volumes inside of it for our OS installation. ## Create LUKS container on the big partition cryptsetup luksFormat /dev/nvme0n0p3 ## Create password ## Open the LUKS container as /dev/mapper/lukslvm cryptsetup open /dev/nvme0n0p3 lukslvm ## Enter password Create shards for LVM Now that the LUKS container is created and unlocked, we can start making a filesystem table inside of it. I'm using parted for this job, because it's easier to make notes of it than screenshotting everything in gparted. Also, gparted doesn't really like LUKS-protected partitions... Here we create a GPT filesystem table and create 15 partitions of variable size. I'm creating them using relative % units rather than absolute GB, because this way parted is able to automatically adjust disk geometry for better performance (usually it's needed to leave some unallocated space in the beginning). ## Make a GPT partition table in the LUKS container parted -s /dev/mapper/lukslvm mklabel gpt ## Open the LUKS container with parted parted /dev/mapper/lukslvm (parted) unit s ## switch to relative units (automatically adjusts geometry for performance) (parted) print ## show all partitions -- should be empty (parted) print free ## show free space slots (parted) mkpart primary 0% 10% ## Create partition (shard) and assign 10% of device's size to it (parted) mkpart primary 10% 20% ## Create partition (shard) and assign 10% of device's size to it (parted) mkpart primary 20% 30% ## Create partition (shard) and assign 10% of device's size to it (parted) mkpart primary 30% 50% ## Create partition (shard) and assign 10% of device's size to it (parted) mkpart primary 40% 50% ## Create partition (shard) and assign 10% of device's size to it (parted) mkpart primary 50% 55% ## Create partition (shard) and assign 5% of device's size to it (parted) mkpart primary 55% 60% ## Create partition (shard) and assign 5% of device's size to it (parted) mkpart primary 60% 65% ## Create partition (shard) and assign 5% of device's size to it (parted) mkpart primary 65% 70% ## Create partition (shard) and assign 5% of device's size to it (parted) mkpart primary 70% 75% ## Create partition (shard) and assign 5% of device's size to it (parted) mkpart primary 75% 80% ## Create partition (shard) and assign 5% of device's size to it (parted) mkpart primary 80% 85% ## Create partition (shard) and assign 5% of device's size to it (parted) mkpart primary 85% 90% ## Create partition (shard) and assign 5% of device's size to it (parted) mkpart primary 90% 95% ## Create partition (shard

So, I got a new work laptop. I'm one of a few people in our company who prefers to set up the laptop myself. And I'm a Linux kind of person, so of course it will be running some flavour of Linux. LinuxMint, to be exact. Why? Because it works OOTB and I get the best out of the Linux world w/o having to spend days tinkering the environment to my liking. Although I'm considering NixOS too... But that's for another time.
Now, over the years of owning a Linux laptop (actually, laptop*S* -- my personal ones are also Linux-driven), I've noticed several patterns making my life easier. One of such is using LVM for disk partitioning. 2 VGs are usually enough: system
and user
. System hosts root and swap (if needed), and user is for.. you know.. all the user stuff, starting with /home.
One of the reasons I really like LVM is that you can easily extend your filesystems if you need more space. And here's the trick: you should NOT assign all your free space from the beginning to leverage this LVM's feature. Here's how I normally do:
- partition the disk into
- EFI
- /boot
- LVM shards -- 4-5x unformatted partitions of 50GB each, and the rest -- ~20GB each
-
pvcreate
all the shards -
vgcreate
bothsystem
anduser
groups with ~40-50GB each (initially) -
lvcreate
bothroot
andhome
volumes, create filesystems - install OS there and use it normally
-
vgextend
+lvextend
the filesystem that's running out of space. IDK which one it will be in advance, so it's really nice to have the flexibility to do this when I need it, w/o planning in advance.
Now here's the rub. It all works OOTB with my personal laptop, but for a work laptop, I must use encrypted storage. And in order to do anything with an encrypted volume, I first have to unlock (open) it.
And this post is a list of what I had to do to make LUKS+LVM work together.
Partitioning
Let's assume we are on an empty device. At first I wasn't, but I nuked all MS Windows-related filesystems right away and started with a tabula rasa.
Using gparted
(or parted
if it's more convenient), create 3 partitions:
- ~256MB in size -- UEFI
- ~10GB in size -- /boot (ext4)
- remaining free space -- LUKS (unformatted)
Suppose, the LUKS partition is /dev/nvme0n0p3 and /boot is /dev/nvme0n0p2
LUKS setup
Create LUKS container
First, we need to create a LUKS container, and then we'll be able to create volumes inside of it for our OS installation.
## Create LUKS container on the big partition
cryptsetup luksFormat /dev/nvme0n0p3
## Create password
## Open the LUKS container as /dev/mapper/lukslvm
cryptsetup open /dev/nvme0n0p3 lukslvm
## Enter password
Create shards for LVM
Now that the LUKS container is created and unlocked, we can start making a filesystem table inside of it.
I'm using parted
for this job, because it's easier to make notes of it than screenshotting everything in gparted
. Also, gparted
doesn't really like LUKS-protected partitions...
Here we create a GPT filesystem table and create 15 partitions of variable size. I'm creating them using relative %
units rather than absolute GB
, because this way parted is able to automatically adjust disk geometry for better performance (usually it's needed to leave some unallocated space in the beginning).
## Make a GPT partition table in the LUKS container
parted -s /dev/mapper/lukslvm mklabel gpt
## Open the LUKS container with parted
parted /dev/mapper/lukslvm
(parted) unit s ## switch to relative units (automatically adjusts geometry for performance)
(parted) print ## show all partitions -- should be empty
(parted) print free ## show free space slots
(parted) mkpart primary 0% 10% ## Create partition (shard) and assign 10% of device's size to it
(parted) mkpart primary 10% 20% ## Create partition (shard) and assign 10% of device's size to it
(parted) mkpart primary 20% 30% ## Create partition (shard) and assign 10% of device's size to it
(parted) mkpart primary 30% 50% ## Create partition (shard) and assign 10% of device's size to it
(parted) mkpart primary 40% 50% ## Create partition (shard) and assign 10% of device's size to it
(parted) mkpart primary 50% 55% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 55% 60% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 60% 65% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 65% 70% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 70% 75% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 75% 80% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 80% 85% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 85% 90% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 90% 95% ## Create partition (shard) and assign 5% of device's size to it
(parted) mkpart primary 95% 100% ## Create partition (shard) and assign 5% of device's size to it
(parted) print free ## show free space slots. Could be a small slot in the beginning of the disk.
(parted) print ## show all the created partitions
Create LVM
Now we have all the shards created. Next, we'll create LVM PhysicalVolumes out of them and construct the rest of the LVM system.
pvcreate /dev/mapper/lukslvm1
pvcreate /dev/mapper/lukslvm2
pvcreate /dev/mapper/lukslvm3
pvcreate /dev/mapper/lukslvm4
pvcreate /dev/mapper/lukslvm5
pvcreate /dev/mapper/lukslvm6
pvcreate /dev/mapper/lukslvm7
pvcreate /dev/mapper/lukslvm8
pvcreate /dev/mapper/lukslvm9
pvcreate /dev/mapper/lukslvm10
...
Let's attach x2 50GB PVs to each VG for initial setup and leave the rest for later in the future, i.e. when we'll really need them.
vgcreate system /dev/mapper/lukslvm1
vgextend system /dev/mapper/lukslvm2
vgcreate user /dev/mapper/lukslvm3
vgextend user /dev/mapper/lukslvm4
lvcreate -L 50G -n root system
lvcreate -L 50G -n home user
pvs
vgs
lvs
Install OS
At this point, we should have LVM set up. Volumes are available, so we can now install the OS.
Select:
/dev/nvme0n0p1 fat32 EFI
/dev/nvme0n0p2 ext4 /boot
/dev/nvme0n0p3 ext4/xfs /home/dev/nvme0n0 bootloader
When the install is complete, do not reboot just yet.
Configure LUKS to be auto-mounted and LVM auto-discovered
Mount and chroot the installed system
mount /dev/mapper/system-root /mnt ## system-root should be created by lvcreate
mount /dev/nvme0n0p2 /mnt/boot
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
mount --bind /run /mnt/run
mount --bind /dev /mnt/dev
chroot /mnt
Now we're more or less inside the OS we've just installed.
Configure LUKS
In order to auto-unlock the LUKS container on boot, we must create the /etc/crypttab file containing information about that container: where to find it (device's UUID), what options to use, and how to name it after unlocking.
lsblk | grep /dev/nvme0n0p3 ## note down the UUID
echo "cryptpool UUID=${UUID} none luks,discard" >/etc/crypttab
## make sure both crypttab and fstab are present and contain valid entries
cat /etc/fstab
cat /etc/crypttab
Configure LVM bootstrap
Now here's the fun part. When Linux is loading, it first extracts and boots into the in-ram filesystem. Then it's the kernel's job to use tools and configurations available in this filesystem to prepare the host for further booting: ensuring hardware, filesystems, drivers, etc, are all in place.
Here's the rub: by default, in initramfs, the kernel will not do both: unlock LUKS and discover LVM. We need to give it a little push. Initramfs should be OK to open the LUKS container: if you boot into your installed OS, it should ask for a password, but after that, you'll be dropped into a busybox shell, as after opening LUKS filesystems are not yet detected.
While still in the LiveUSB system and in chroot, create this:
cat <<-'EOF' >/etc/initramfs-tools/scripts/local-top/kpartx
#!/bin/sh
PREREQ=""
prereqs() {
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /scripts/functions
# checking if lukslvm is already present
if [ ! -e /dev/mapper/lukslvm ]; then
echo "lukslvm still unavailable"
exit 1
fi
echo "[initramfs] running kpartx lukslvm"
kpartx -av /dev/mapper/lukslvm
vgchange -ay
exit 0
EOF
This script above will be run by the kernel in initramfs. local-top/ scripts will be launched after LUKS is unlocked. This script launches kpartx
to rediscover partitions after luks-unlocking (there's no udev daemon -- new devices are not detected automatically). Then it does an LVM scan with vgchange
to make sure all the LVM volumes become available to the system.
Make sure to make it executable!!
chmod +x /etc/initramfs-tools/scripts/local-top/kpartx
And then pack it into the initramfs of all the available kernels:
update-initramfs -u -k all
and... reboot. If everything's done right, you should see your system prompting for a LUKS password, then an OS logo should appear, and then you should be booted into the OS login screen.
If something did not work as expected, you will likely be dropped into the (initramfs) busybox shell. You can try and make tweaks there, but they will only be ephemeral, i.e. they will disappear after a reboot. To continue booting, simply exit the busybox shell.
References:
- https://blog.hqcodeshop.fi/archives/273-GNU-Parted-Solving-the-dreaded-The-resulting-partition-is-not-properly-aligned-for-best-performance.html
- https://forums.linuxmint.com/viewtopic.php?t=420706
- https://manpages.ubuntu.com/manpages/xenial/man8/initramfs-tools.8.html
Written with StackEdit.