Well, I managed to finalise this migration, although it took a couple of attempts.
Summary of approach
I didn’t end up taking any of the approaches I originally contemplated. Instead, my approach involved:
- Creating the new partition scheme on the new SSDs (EFI boot partition plus root partition)
- Installing the UEFI bootloaders on the EFI boot partitions
- Creating a RAID 1 array on the root partitions
- Creating an LVM physical volume using the RAID 1 array and creating an LVM volume group using that volume
- Creating an LVM root logical volume on that volume group
- Copying the contents of the root partition from the old SSDs to the new SSDs
- Creating an LVM swap logical volume on the volume group on the RAID 1 array
- Tidying up
My first attempt involved manually creating the partitions and manually installing the UEFI bootloaders on those partitions. After a few minor missteps along the way, that ended up with a bootable system on the new SSDs, but using the wrong bootloader (the fallback BOOTX64.EFI
rather than the Ubuntu grubx64.efi
or shimx64.efi
bootloader).
It’s possible that I could have fixed this by implementing step 27 below, but that was a step I didn’t discover until implementing my second attempt. But see now the addendum to this answer.
So I started the whole process afresh.
The second attempt involved using the Ubuntu Live USB to perform a fresh install on one of the new SSDs (with my new partition scheme, but with no RAID or LVM). I figured that this would give me an EFI boot partition that reflected the Ubuntu default (size, contents).
This approach also gave me the opportunity to note what parameters a default install included in /etc/fstab
, so I could properly replicate that on my new install.
Once the default install was completed by the Ubuntu Live USB, I copied the EFI boot partition to the other SSD. I then removed the installed root partition and replaced it with a new one, to wipe the installed OS. I then replicated that partition on the other SSD, and proceeded with the rest of my steps (creating the RAID array, etc).
As you will see, the result of this was a system that didn’t initially boot, but I managed to rescue it from grub
and now all is well.
Read on for the detailed steps. I must admit that to some degree those steps are a bit of a reconstruction of what I did, as I had a few little twists along the way, but fundamentally what I have outlined below is an accurate reflection of the substance of what I did.
References
In preparing for and implementing my two attempts, I had reference to a few tutorials, including in particular:
None exactly matched my use case, and some of the steps outlined actually gave me problems, but they did help in various ways.
All the details
To set the scene:
- My install is Ubuntu 18.04.3 LTS
- My old install was on
/dev/md0
, with a swap partition on /dev/md1
, both on my old SSDs
- I also had a RAID 6 data array (
/dev/md2
) on separate HDDs, which I wanted to preserve with the new install
- My new SSDs were
/dev/nvme0n1
and /dev/nvme1n1
Now for the steps:
Step 1
This first step I didn’t actually do, but in hindsight it may have helped. It involves preparing my old Ubuntu install (the one I would eventually copy over) by installing some packages that would be needed when running under UEFI. So, when booted into my old install:
$ sudo apt-get update
$ sudo apt-get install grub-efi-amd64 efibootmgr
Step 2
Before proceeding, I disabled the swap partition on the old install, just to avoid any legacy settings being carried over when the root partition contents were copied. I'm not sure whether that would have been an actual issue, but wanted to be sure:
$ sudo swapoff /dev/md1
I then updated /etc/fstab
in my favourite text editor to comment out the swap entry, and then:
$ sudo mount -a
Step 3
Then I booted into the Ubuntu Live USB (the Desktop version, not the Server version, although my install is Server) in UEFI mode, and selected ‘Install Ubuntu’. You may need to adjust your mobo firmware settings to ensure you boot in UEFI mode.
I suppose I could have used the Server version for this step as well, but given I needed the Desktop version for later steps I decided to keep things simple.
I chose the guided install, selecting to use my first new SSD (/dev/nvme0n1
) and to install using the entire drive. I did not set up RAID or LVM. This resulted in (a) an EFI boot partition, and (b) a root partition, taking up the rest of the drive, on which Ubuntu Desktop was installed.
Step 4
With that done, I rebooted into the Ubuntu Live USB in UEFI mode, this time selecting ‘Try Ubuntu without installing’. You can test that you are in UEFI mode by opening a terminal (Ctrl-Alt-T) and running:
$ ls /sys/firmware/efi
If this shows a number of files, you are in UEFI mode.
Step 5
While at the terminal prompt, I upgraded Ubuntu Live and installed required packages:
$ sudo apt-get update && sudo apt-get dist-upgrade
$ sudo apt-get install -y mdadm
Step 6
I then checked the partition scheme of the new install so that I could properly replicate it on the other SSD:
$ sudo parted /dev/nvme0n1
(parted) unit MiB print
Model: Samsung SSD 970 EVO Plus 500GB (nvme)
Disk /dev/nvme0n1: 476940MiB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1.00MiB 513MiB 512MiB fat32 EFI System Partition boot, esp
2 513MiB 476939MiB 476426MiB ext4
(parted) quit
This showed that the default EFI partition was 512 MiB in size, starting 1 MiB from the beginning of the drive, with a FAT32 filesystem.
Step 7
I then also checked the default mount parameters in /etc/fstab
(UUIDs masked below as 'NUMBER'):
$ sudo mkdir -p /mnt/newroot
$ sudo mount /dev/nvme0n1p2 /mnt/newroot
$ cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
## / was on /dev/nvme0n1p2 during installation
UUID=NUMBER / ext4 errors=remount-ro 0 1
/boot/efi was on /dev/nvme0n1p1 during installation
UUID=NUMBER /boot/efi vfat umask=0077 0 1
/swapfile none swap sw 0 0
$ sudo umount /mnt/newroot
I took a note of the parameters so that I could replicate them later.
Step 8
I then deleted the installed root partition on my first SSD, to remove the Desktop install, and created a fresh partition (same size) in its place. Note that I didn't create it with any filesystem type given that a RAID array and LVM volume group would be overlaid on top:
$ sudo parted -a optimal /dev/nvme0n1
(parted) rm
Partition number? 2
(parted) mkpart primary 513MiB 100%
(parted) name 2 "Ubuntu Filesystem"
(parted) align-check optimal 2
(parted) set 2 raid on
Step 9
While still in the parted interactive prompt, I then selected the other new SSD (/dev/nvme1n1
) to replicate the partition scheme:
(parted) select /dev/nvme1n1
(parted) mklabel gpt
(parted) mkpart primary fat32 1MiB 513MiB
(parted) set 1 esp on
(parted) set 1 boot on
(parted) mkpart primary 513MiB 100%
(parted) name 2 "Ubuntu Filesystem"
(parted) align-check optimal 2
(parted) set 2 raid on
(parted) unit MiB print
Model: Samsung SSD 970 EVO Plus 500GB (nvme)
Disk /dev/nvme1n1: 476940MiB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1.00MiB 513MiB 512MiB fat32 EFI System Partition boot, esp
2 513MiB 476939MiB 476426MiB ext4 Ubuntu Filesystem raid
(parted) quit
Step 10
I then created a RAID 1 array on the root partitions, /dev/nvme0n1p2
and /dev/nvme1n1p2
. I called it /dev/md3
to distinguish it from the RAID arrays already created under my old system and to avoid naming conflicts during the migration, with the aim of renaming it later when the new system was up and running:
$ sudo mdadm --create --bitmap=internal /dev/md3 --level=1 --raid-devices=2 /dev/nvme0n1p2 /dev/nvme1n1p2
I waited until the RAID array had finished syncing before proceeding, by checking:
$ cat /proc/mdstat
Step 11
I then created an LVM physical volume out of the RAID 1 array, with a single volume group (called vgmain
) using that physical volume, and then an LVM root logical volume (called lvroot
and with size of 100 GiB) on that volume group:
$ sudo pvcreate /dev/md3
$ sudo vgcreate vgmain /dev/md3
$ sudo lvcreate -L 100G -n lvroot vgmain
Step 12
I then created an EXT4 filesystem on the root logical volume:
$ sudo mkfs.ext4 -b 4096 -E stride=128,stripe-width=256 /dev/vgmain/lvroot
Step 13
At this point I assembled the old RAID devices so that my old root partition (/dev/md0
) was available to copy data from. Obviously to do this my old SSDs still had to be connected and powered up:
$ sudo mdadm --assemble --scan
Step 14
Once assembled, I mounted the old root partition:
$ sudo mkdir -p /mnt/oldroot
$ sudo mount /dev/md0 /mnt/oldroot
Step 15
I also mounted the new root partition (remember the mountpoint had been created previously in step 7):
$ sudo mount /dev/vgmain/lvroot /mnt/newroot
Step 16
Now I could copy the contents of the old root partition to the new one:
$ sudo rsync -axHAX --progress /mnt/oldroot/ /mnt/newroot
Step 17
Once completed, the old root partition could be unmounted and the old RAID array stopped:
$ sudo umount /mnt/oldroot
$ sudo mdadm --stop /dev/md0
Step 18
A digression now. In hindsight the boot issue I encountered at the end might have been avoidable if I had performed step 27 at this point in time. To do so, I would have done the following:
$ sudo mkdir -p /mnt/newroot/boot/efi
$ sudo mount /dev/nvme0n1p1 /mnt/newroot/boot/efi
$ for d in /dev /dev/pts /sys /proc; do sudo mount -B $d /mnt/newroot$d; done
$ sudo chroot /mnt/newroot
# grub-install
# update-grub
# umount /dev/nvme0n1p1
I'm not sure if that would have fixed the issue encountered later, but there it is. But see now the addendum to this answer.
Step 19
Back to what I actually did. Having copied over the contents of the old root partition to the new root partition (which of course were mirrored in the RAID 1 array), I needed to copy the EFI boot partition contents from the first SSD to the second SSD:
$ sudo dd if=/dev/nvme0n1p1 of=/dev/nvme1n1p1
To check that this was successful, I checked that the UUID on each was the same after completion:
$ sudo blkid
Step 20
For completeness I also wanted to get the second SSD into the boot chain, to ensure that I could happily boot from either SSD. At this stage the new root partition was still mounted, but I needed to mount other directories from the Ubuntu Live USB as well (I would not have had to mount those here if I had implemented step 18):
$ for d in /dev /dev/pts /sys /proc; do sudo mount -B $d /mnt/newroot$d; done
$ sudo chroot /mnt/newroot
# efibootmgr -v
Comment: The above command showed me the file entry for "ubuntu", which in my case was
\EFI\ubuntu\grubx64.efi (I could have also used \EFI\ubuntu\shimx64.efi).
I then used that in the next command
efibootmgr -c -d /dev/nvme1n1 -p 1 -L ubuntu -l '\EFI\ubuntu\grubx64.efi'
Obviously, the effect of a chroot
to the new SSDs was that I entered that environment as if I was booted into it, so commands I ran while in the jail were executed on the SSDs, rather than on the Ubuntu Live USB. And in a chroot
jail, commands are run as root (as indicated by the ‘#’ prompt), so sudo
is not required. Although of course given that the SSDs were not actually booted, I had to mount some key directories from the Ubuntu Live USB to give me the required functionality.
(As an aside, I have to note that, as I had not actually done step 1, when I attempted to run efibootmgr
I found it wasn't installed, because I had copied across my legacy install which was a MBR/BIOS installation. So I had to install it at this stage. And to do so, I found I also need to mount /run
from the Ubuntu Live USB, to give me internet access. I also installed grub-efi-amd64
.)
Step 21
I also checked that the boot order was correct, ie that /dev/nvme0n1p1
was first in the boot chain, and /dev/nvme1n1p1
was second, by checking the details returned by efibootmgr -v
. If necessary, the order can fixed by running the following command with the 4 digit code for each relevant disk or partition, in the order required, for example:
# efibootmgr -o 0000,0001,0002,0005,0004
Step 22
I then updated /etc/fstab
with my favourite text editor to include the new UUIDs for the root mountpoint and the/boot/efi
mountpoint (as determined from blkid
). I used the parameters from step 7, adding noatime
to the root mountpoint (consistent with my old installation).
Of course I kept the mountpoint entry for my old data RAID 6 array, as that would continue to be used with the new install, as well as a mountpoint securing /tmp
.
Step 23
The configuration details for mdadm on the new SSDs also needed to be updated, which I did by adding the details for the new array /dev/md3
via the command below, and then manually editing /etc/mdadm/mdadm.conf
with my text editor to ensure that the legacy entries for the old /dev/md0
and /dev/md1
were removed:
# mdadm --examine --scan >> /etc/mdadm/mdadm.conf
Once the configuration file was cleaned up, I ran:
# update-initramfs -u
Step 24
I was essentially done at this point (or so I thought). So for a clean completion, I exited from chroot
and unmounted everything:
# exit
$ for d in /dev /dev/pts /sys /proc; do sudo umount /mnt/newroot$d; done
$ sudo umount /mnt/newroot
Step 25
Finally, the moment of truth - I removed the Ubuntu Live USB, and rebooted the system, checking in my mobo firmware that the boot order had my first new SSD at the top, so that I would boot from /dev/nvme0n1p1
. If necessary, you can check when booted which drive has been booted from, by running efibootmgr -v
.
Step 26
At this point, though, I had my boot issue. The system wouldn't boot, and stopped at the grub
prompt. To fix this, I did the following at the prompt:
grub> ls
Comment: The above command showed me the available partitions and directories, so I knew
what my root partition was called and could use it in the next command
grub> set root=(lvm/vgmain-lvroot)
grub> linux /vmlinuz root=/dev/mapper/vgmain-lvroot
grub> initrd /initrd.img
grub> boot
Step 27
Doing this got me booted into the new SSDs. At that point, I ran the following, which might not have been necessary if I had done these at step 18:
$ sudo grub-install
$ sudo update-grub
I found that after running these commands, the size of grubx64.efi
in /boot/efi/EFI/ubuntu
changed, so it appears there was something amiss with the original bootloader installation. But see now the addendum to this answer.
Step 28
I then repeated the relevant commands in steps 19 and 20 to copy the contents of the updated EFI boot partition from /dev/nvme0n1p1
to /dev/nvme1n1p1
, and to update the boot chain. If you find you have legacy entries that also need to be deleted, use:
$ sudo efibootmgr -b 0003 -B # Eg to delete entry Boot0003
Step 29
Finally I re-created my swap partition, this time as an LVM logical volume:
$ sudo lvcreate -L 16G -n lvswap vgmain
$ sudo mkswap /dev/vgmain/lvswap
$ sudo swapon /dev/vgmain/lvswap
And updated /etc/fstab
with the relevant UUID and the parameters determined in step 7:
UUID=NUMBER none swap sw 0 0
And that was it! I have now a fully migrated and functioning system. I will eventually rename my new RAID 1 to md0
(for OCD reasons only), but that is for another day.
Addendum
Item 1
The preparatory tutorials I studied before attempting the migration said that Secure Boot should be disabled on the mobo firmware before starting. I did not do that, as I found that my mobo (Asus) made it difficult to do so (I have since researched it and found it can be done by deleting the Secure Boot keys). It’s possible that my boot issues were caused by Secure Boot still being operative.
Item 2
You will find when booting your migrated system in UEFI mode that it pauses for 30 seconds at the grub menu before proceeding. This is apparently to give the user time to access the menu, as in certain circumstances it may otherwise become inaccessible. If you are annoyed by the additional delay to the boot time, you can reduce this by adding the following to /etc/default/grub
:
GRUB_RECORDFAIL_TIMEOUT=5 # Eg to reduce to five seconds
Don’t reduce it to zero. And then:
$ sudo update-grub