Moving an installation to a different drive and encrypting it with LUKS - a guide

Difficulty: ★★☆☆☆

This guide shows how to move your current installation of Manjaro to a new drive and encrypt it with LUKS.

I’ve prepared it based on what I’ve been doing with my own laptop, so there’ll be some references to my own setup.
It assumes your system has UEFI (instead of BIOS) and your partition table is GPT (and not MBR).

About the boot partition

My system had three partitions:

  • /dev/sda1 - the EFI partition
  • /dev/sda2 - the /boot partition, holding the GRUB config and Linux kernels
  • /dev/sda3 - the root partition, holding the Manjaro installation and my data

You can check out yours with lsblk.

Why a separate /boot?

  • Your boot time will be shorter if GRUB will be unencrypted (it resides in /boot)
  • if you dual(or triple, or whatever the number)-boot your machine with multiple Linux installs, they can share /boot, so you don’t need to have all installs duplicating the same files to /boot in their partition; only one at a time would be picked for the computer boot-up, anyway.

If you don’t have a dedicated partition for /boot and you want to create one like me
(it’s assumed in the guide, that you do), just copy the files from your /boot (skipping /boot/efi) to the external drive. They’ll be used later.

If you don’t want separate /boot, you might need to set up GRUB and probably the initial RAM disk differently than how I show here.
That’s because /boot (which hosts GRUB) will also be encrypted1.

For that setup you’ll need to change /etc/default/grub in the later part of this tutorial:

  • uncomment or add GRUB_ENABLE_CRYPTODISK=y
  • include the cryptodisk=<KEY> directive to GRUB_CMDLINE_LINUX_DEFAULT
  • maybe2 add loading of the LVM kernel module to GRUB_CMDLINE_LINUX_DEFAULT

I created that setup before the current one, but I wasn’t satisfied with the boot time,
and I didn’t want to spend more time on investigating GRUB. That would be necessary if I were to cut down the number of hashing rounds for encryption, so that it doesn’t have that >10 second wait on boot.

Can you put the new drive side-by-side with the old one?

First you need to boot the laptop from a live USB, so that the system you want to copy isn’t running.

No, I have only a single drive-slot.

Copy your current partitions to an external drive with dd, like so3:

sudo dd if=/dev/sda1 of=/run/media/manjaro/the_external_drive/efi_partition.bin bs=4M status=progress
sudo dd if=/dev/sda2 of=/run/media/manjaro/the_external_drive/boot_partition.bin bs=4M status=progress
sudo dd if=/dev/sda3 of=/run/media/manjaro/the_external_drive/root_partition.bin bs=4M status=progress

Yes, I have multiple drive-slots.
Go through the next section about partitioning first, then use dd to copy the partitions directly:

# assuming "sda" is the old drive and "sdb" is the new drive; the names can also look like "nvme0"
sudo dd if=/dev/sda1 of=/dev/sdb1 bs=4M status=progress
sudo dd if=/dev/sda2 of=/dev/sdb2 bs=4M status=progress
sudo dd if=/dev/sda3 of=/dev/sdb2 bs=4M status=progress

Partitioning the new drive

Partition the drive, so you can copy the old partitions to it. I used GParted for that, you can use fdisk, KDE Partition Manager, or whatever you want, but I’m not gonna show how to do it. Shouldn’t be a big problem, though :slight_smile:

The types/filesystems of the partitions you create don’t matter, because they’ll be overwritten when being copied to with dd.

If you didn’t start going through this tutorial with a partition for /boot, please create a 512 megabyte (or however big you deem sensible) ext4 partition for it. You’ll be able to copy your old files to it when we mount it later on.

You should probably set the flags boot and esp flags on your EFI partition. Flags are kept in the partition table, not in the partitions themselves. I don’t know if these flags are strictly necessary, though4.

After the partitioning, I ended up with this layout (from lsblk):

 NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
 # ...omitted lines...
 # manjaro live USB
 sda           8:0    1  28.7G  0 disk /run/miso/bootmnt
 ├─sda1        8:1    1   2.4G  0 part
 └─sda2        8:2    1     4M  0 part
 # the external drive holding the partition images
 sdb           8:16   0   1.8T  0 disk
 └─sdb1        8:17   0   1.8T  0 part /run/media/manjaro/Seagate Backup Plus Drive
 # the new drive
 nvme0n1     259:0    0 953.9G  0 disk
 ├─nvme0n1p1 259:4    0   200M  0 part  # will be /boot/efi
 ├─nvme0n1p2 259:5    0   512M  0 part  # will be /boot
 └─nvme0n1p3 259:6    0 953.2G  0 part  # will hold the encrypted root partition

Preparing LVM in LUKS

Note that all (or most) the code here is also available as scripts on my repo - moving partitions to the new layout, mounting for chroot, adjusting configs and building GRUB.

First, set up some variables you’ll be using throughout the process. You’ll need change these so they match your system and personal preferences, of course.

export EFI_PARTITION=/dev/nvme0n1p1
export BOOT_PARTITION=/dev/nvme0n1p2
# this partition will be the LUKS container, holding the LVM and your Linux install
export PARTITION_FOR_LUKS=/dev/nvme0n1p3
# name of the LVM group we'll create
export VG_NAME=vg0
# name of the LVM volume that will host the system
export OS_VOLUME_NAME=manjaro

Make the chosen partition into a LUKS container:

sudo cryptsetup luksFormat --type luks1 $PARTITION_FOR_LUKS

I’m using luks1 instead of luks2, because the latter isn’t handled by GRUB yet.

Now we need to open the newly created LUKS container (you can use any other name than crypt, of course)

sudo cryptsetup luksOpen $PARTITION_FOR_LUKS crypt

What people usually recommend (e.g. paragraph 2.1.6 from cryptsetup's FAQ) is to fill the LUKS container with zeroes, so that it looks like random data on the drive, but because that doesn’t seem so dangerous I’m skipping that step.

Next, mark the opened LUKS container as a “physical volume” for LVM, and create the LVM group:

sudo lvm pvcreate /dev/mapper/crypt
sudo vgcreate $VG_NAME /dev/mapper/crypt

And finally, create the LVM volume that will host your OS. I’ve chosen a size (853 gigabytes) that will leave me 100 gigs for creating a new volume with another Linux install in the future for experiments:

sudo lvcreate -L 853G -n $OS_VOLUME_NAME $VG_NAME

Now, lsblk (for me) reports this layout:

 nvme0n1           259:0    0 953.9G  0 disk
 ├─nvme0n1p1       259:4    0   200M  0 part
 ├─nvme0n1p2       259:5    0   512M  0 part
 └─nvme0n1p3       259:6    0 953.2G  0 part
   └─crypt         254:0    0 953.2G  0 crypt
     └─vg0-manjaro 254:1    0   853G  0 lvm

Restoring the old partitions on the new drive

Now, cd into the place where you’ve stored the partition images, and copy them over to the new partitions one by one:

export OS_VOLUME_PATH=/dev/${VG_NAME}/${OS_VOLUME_NAME}
sudo dd if=efi_partition_image.bin of=$EFI_PARTITION bs=8M status=progress
sudo dd if=boot_partition_image.bin of=$BOOT_PARTITION bs=8M status=progress
sudo dd if=root_partition_image.bin of=${OS_VOLUME_PATH} bs=8M status=progress

In my case, the root partition has more space than it had on the previous drive. Because a filesystem retains its size when copied over with dd, it won’t be making use of that additional space. That’s how you expand the (ext4) filesystem so that it takes all the space available in the volume:

sudo resize2fs ${OS_VOLUME_PATH}

If for the EFI and boot partitions you’ve also allocated more space than they had on the previous drive,
you can enlarge their filesystems accordingly:

sudo resize2fs $EFI_PARTITION
sudo resize2fs $BOOT_PARTITION

Chrooting into the old Linux install

If you’d reboot the system now and tried booting from the EFI partition, you’d fail. We still need to adjust some files.

To do that, we first need to mount the partitions we’ve created for the live Linux we’re working in:

sudo mount $OS_VOLUME_PATH /mnt
sudo mount $BOOT_PARTITION /mnt/boot
sudo mount $EFI_PARTITION /mnt/boot/efi

You’ll be able to browse your old files in /mnt right now.

Next you need to bind-mount some pseudo-directories:

sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys /mnt/sys
sudo mount --bind /dev /mnt/dev
sudo mount --bind /dev/pts /mnt/dev/pts

Now, everything’s prepared for using chroot to “get inside” that old install:

sudo chroot /mnt

You should now have a root shell running in your old Linux system.

Modifying the “tab” files

Now that you’re chrooted into your old Linux install, we need to fixup a few things in order to make it bootable.

First, run blkid command to get the UUID of the partition hosting your LUKS container. For me, the output looks like this:

 /dev/loop1: TYPE="squashfs"
 /dev/mapper/vg0-manjaro: UUID="e980c99e-acb7-4264-9ed3-300f65694b42" BLOCK_SIZE="4096" TYPE="ext4"
 /dev/nvme0n1p3: UUID="050a93bf-d0d3-4d01-83c7-b65d060d2cc5" TYPE="crypto_LUKS" PARTUUID="e18e4927-c79d-4283-a1b9-e2f41cb92a2d"
 ...omitted...

Because my LUKS container is hosted on /dev/nvme0n1p3, I note the UUID 050a93bf-d0d3-4d01-83c7-b65d060d2cc5. This UUID needs to be added to /etc/crypttab file (you can use nano or vim to edit the files), to instruct the booting system to open the container. The line in the file will look like this:

crypt UUID=050a93bf-d0d3-4d01-83c7-b65d060d2cc5 none luks

As previously, I’m using crypt as the name under which the unencrypted partition will appear.

Now, let’s edit /etc/fstab. Because the UUIDs of all the partitions are copied over from the images you might not need to do that, but I wanted the root filesystem (/) mount to clearly point to the LVM volume containing the Linux install (/dev/vg0/manjaro). My full fstab looks like this:

 UUID=7E5B-9C2C                            /boot/efi      vfat    defaults,noatime 0 2
 UUID=4f3c8672-650d-4a8b-9697-1817ec53bb78 /boot          ext4    defaults,noatime,discard 0 2
 /dev/vg0/manjaro                          /              ext4    defaults,noatime,discard 0 1
 tmpfs                                     /tmp           tmpfs   defaults,noatime,mode=1777 0 0
 /swapfile                                 none           swap    defaults 0 0

Rebuilding the initial RAM disk and GRUB

We need to make sure that the initial RAM disk that bootstraps your system during boot can work with LUKS and LVM. So you need to add lvm2 and encrypt modules to the HOOKS variable in /etc/mkinitcpio.conf. I saw advice saying they should be after keyboard and before filesystems.
That’s how the variable looks for me:

HOOKS="base udev autodetect modconf block keyboard keymap encrypt lvm2 filesystems"

Next, add a cryptdevice instruction pointing to your LUKS partition (should be the same UUID as in your crypttab), to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub. On my system (notice other parameters mindlessly stolen from a default encrypted Manjaro install):

GRUB_CMDLINE_LINUX_DEFAULT="cryptdevice=UUID=050a93bf-d0d3-4d01-83c7-b65d060d2cc5:crypt apparmor=1 security=apparmor udev.log_priority=3"

Now, run:

mkinitcpio -P

Now, something that will be needed so that GRUB can work with EFI 5:

mount -t efivarfs efivarfs /sys/firmware/efi/efivars

Prepare the GRUB config:

grub-mkconfig -o /boot/grub/grub.cfg

For me, it gives this output:

 Generating grub configuration file ...
 Found theme: /usr/share/grub/themes/manjaro/theme.txt
 Found linux image: /boot/vmlinuz-5.4-x86_64
 Found initrd image: /boot/amd-ucode.img /boot/initramfs-5.4-x86_64.img
 Found initrd fallback image: /boot/initramfs-5.4-x86_64-fallback.img
 Warning: os-prober will be executed to detect other bootable partitions.
 Its output will be used to detect bootable binaries on them and create new boot entries.
 grub-probe: error: cannot find a GRUB drive for /dev/sdb1.  Check your device.map.
 grub-probe: error: cannot find a GRUB drive for /dev/sdb1.  Check your device.map.
 Adding boot menu entry for UEFI Firmware Settings ...
 Found memtest86+ image: /boot/memtest86+/memtest.bin
 done

Notice that error with /dev/sdb1 which is the previous location of one of the partitions. I can’t figure out how to fix it or how to “check my device.map”, but that doesn’t seem to be affecting anything.

And now, the final step - install GRUB to the EFI partition:

# the actual partition might be different for you, of course
grub-install /dev/nvme0n1p1

My output:

 Installing for x86_64-efi platform.
 Installation finished. No error reported.

And that’s it! The system should be ready to go.

Now reboot your computer. You should see GRUB, after picking your OS you will be asked for the passphrase for your LUKS container, and after that your old system should boot normally.


Footnotes:

  1. To my knowledge, it’s not possible to have the EFI partition encrypted as well.
  2. I don’t remember if that was really necessary, and I don’t have the setup to test it anymore.
  3. If you’re interested in some discussion about the bs parameter, you can check out this.
  4. Oh no, cargo culting! …But I really don’t wanna spend more time on this (I spent a lot already), to see if the OS wouldn’t boot without the flags.
  5. I got this trick from here
1 Like