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

Apr 8, 2025 - 12:33
 0
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) 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:

Written with StackEdit.