35 KiB
Second Ubuntu desktop boot disk
- Introduction
- Initial configuration and goal
- Step 1: Destination disk partitioning
- Step 2 : First copies
- Step 3 : Destination disk setup after initial copy
- Some problems with what was done
- script to ease subsequent copies
- Links
Introduction
It maybe happened to you already : A disk failure.
Or you are unsure of a new distribution upgrade, and you prefer to keep an operational one in case of.
Or you want to try a new distribution while keeping the old one, or simply test a new kernel, such as linux-next.
Of course, I have already a daily backup (on a different machine), but it does not allow to easily recover in case of serious system issue.
I considered having a mirrored system disk, but it is useful only in case of disk failure, surely not in case of human mistakes.
This document is a step-by-step explanation of the creation of such second (or third) bootable disk, from the copy to the kernel generation. It should work as-is on any debian-based distribution.
Initial configuration and goal
My desktop configuration is described below :
DONE Disks
I have 3 500Gb disks, /dev/sda
, /dev/sdb
, /dev/sdc
. Only one is initially used, /dev/sdb
, which contains the following partitions :
$ sudo sgdisk -p /dev/sdb
Disk /dev/sdb: 976773168 sectors, 465.8 GiB
Model: SAMSUNG HD501LJ
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 6BAC802E-87B0-4DA6-9E60-E008558C6948
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 976773134
Partitions will be aligned on 2048-sector boundaries
Total free space is 2029 sectors (1014.5 KiB)
Number Start (sector) End (sector) Size Code Name
1 2048 1015807 495.0 MiB EF00 efi1
2 1026048 42969087 20.0 GiB 8300 root1
3 42969088 51357695 4.0 GiB 8200 swap1
4 51357696 976773119 441.3 GiB 8300 export1
5 1015808 1026047 5.0 MiB EF02 bios1
$ lsblk -l -o PATH,TYPE,PARTFLAGS,SIZE,FSTYPE,PTTYPE,LABEL /dev/sdb
PATH TYPE PARTFLAGS SIZE FSTYPE PTTYPE LABEL
/dev/sdb disk 465.8G gpt
/dev/sdb1 part 495M vfat gpt EFI1
/dev/sdb2 part 20G ext4 gpt root1
/dev/sdb3 part 4G swap gpt swap1
/dev/sdb4 part 441.3G ext4 gpt export1
/dev/sdb5 part 5M gpt
What is important here:
- Partitioning is GPT
- All partitions have a name (LABEL column) which ends with "1". This number means simply "my first disk". It is not linked to an hardware position
- Partitions look strange : (EFI1 and partition 5) exist as I currently boot with BIOS, but plan to migrate to UEFI boot when I have time (this is not as simple as I originally thought).
Looking at /etc/fstab
, corresponding lines are :
$ grep '[[:alpha:]+]1[[:space:]]' /etc/fstab
LABEL=EFI1 /boot/efi.old vfat noauto,defaults 0 2
LABEL=root1 / ext4 errors=remount-ro 0 1
LABEL=export1 /export ext4 defaults 0 2
LABEL=swap1 none swap nofail,sw,pri=0 0 0
DONE Automount
The automounter (see autofs(8)
), when configured, allows to automagically mount/umount file systems according to rules.
As an example, we can decide that a directory /mnt/hdd
could handle all partitions with a given label (you may choose different rules).
For example, if a given partition label is FOO
, you could just do a ls /mnt/hd/foo
to view its contents. In this case, the automounter will mount the partition before the execution of the ls
command. This mount willl be released (un-mounted) later, after some time.
As an example, my autofs(8)
configuration is :
/mnt/hd /etc/auto.hd --timeout=120 --ghost
This line says :
- /mnt/hd is managed by autofs,
- The automounter rules for this directory are in
/etc/auto.hd
, - The mounts will be automatically unmounted if not accessed for 120 seconds,
- the
--ghost
option says that the automounter will create directories in/mnt/hd
, without having to access their contents.
See auto.master(5)
for full details.
,* -fstype=auto,defaults :LABEL=&
Here, we say that for any X ('*
') access in /mnt/hd, the partition with a label X will be mounted with the options specified.
See autofs(5)
for the description of automounter maps.
Practical example : If a partition's LABEL is export2
, when a processus will try to access /mnt/hd/export2
, that partition will be automagically mounted. The partition will be un-mounted 2 minutes after being not used.
Demonstration : Let say, with the above configuration, that I have a partition /dev/sdb4
, with the label export1
.
$ ls -l /mnt/hd
total 0
drwxr-xr-x 2 root root 0 Oct 13 21:39 EFI1
drwxr-xr-x 2 root root 0 Oct 13 21:39 EFI2
drwxr-xr-x 2 root root 0 Oct 13 14:05 export1
drwxr-xr-x 2 root root 0 Oct 13 01:08 export2
drwxr-xr-x 2 root root 0 Oct 13 02:00 porsche1
drwxr-xr-x 2 root root 0 Oct 13 01:01 root1
drwxr-xr-x 2 root root 0 Oct 13 01:01 root2
$ mount -l | grep sdb4
/dev/sdb4 on /export type ext4 (rw,relatime) [export1]
$ ls -l /mnt/hd/export1
total 32
drwxr-xr-x 8 root root 4096 Oct 2 06:47 home
drwx------ 2 root root 16384 May 20 2018 lost+found
drwxr-xr-x 4 root root 4096 Aug 16 08:50 to-classify
$ mount -l | grep sdb4
/dev/sdb4 on /export type ext4 (rw,relatime) [export1]
/dev/sdb4 on /mnt/hd/export1 type ext4 (rw,relatime) [export1]
$ sleep 120
$ mount -l | grep sdb4
/dev/sdb4 on /export type ext4 (rw,relatime) [export1]
DONE Goal
I will here duplicate /dev/sdb
(my disk 1) on /dev/sda
(disk 2), and install grub
on sda
(to make it bootable). Eventually, a script will allow to automate subsequent updates (for instance to run it on a daily basis).
DONE Ensure destination disk is not in use
In this document, SRC
and DST
variables will be the source and destination disks. In my case : /dev/sdb
, and /dev/sda
.
export SRC=/dev/sdb
export DST=/dev/sda
It is necessary to check that DST
is not used. It may be for instance used for:
- A mount point (try
sudo mount | grep ${DST}
) - A swap device (try
swapon | grep ${DST}
) - By automount
- By another operating system if you have a multi-boot systemavez un multi-boot
- By a database in raw mode, or more generally by any application using directly a raw device
- etc…
Be sure to understand these dependancies before goint further.
Once done, you have to umount/"swapoff" (see umount(8)
and swapoff(8)
for details) anything related to DST
, and remove any reference to this device in fstab
, database configuration, etc.
Step 1: Destination disk partitioning
DONE Basic concepts
DONE Disk partitioning (partition table)
This is a table, stored on disk, which describes the different partitions. Most common nowadays are :
- GPT : GTP is used by default on most systems nowadays (Linux, MacOS, Windows).
- DOS/MBR : Used on end-of-century operating systems (DOS, Windows). MBR could be found on old machines, and maybe on new disks. You probably remember the "primary/secondary partition" jargon ?
- And many others…
DONE Partition types
This is the format of an individual partition, within the disk itself. On GNU/Linux, you will use mostly :
- Extended File System : The most common nowadays. The last version of this family, EXT4, if often the default on many distributions, such as Debian, Ubuntu etc…
- BTRFS : Designed by Oracle, and relatively recent, this file system has some interesting features, such as snapshots, integration of some RAID configurations, atomic file cloning, etc… It is the default file system on Fedora since version 33 (October 2020).
- HFS and HFS+ : If you have a MaxOS machine, for instance dual-booting MacOS/Linux, you will probably have have some HFS+ partitions.
- FAT, VFAT, FAT32, exFAT etc… : The different types/extensions of Microsoft's FAT file system. You will often find them by default on external disks, as they are well supported by most Operating systems. FAT is also used in EFI specifications to host the boot system.
- SWAP : As its name says.
- etc…
DONE Partitions label
Partitions often have a name (you can see it for instance when you insert a CD, an external disk), etc… This naming can be confusing, as for example, if you use gparted
, you will see one Label
column and one Name
column.
- Label : This is a name which resides inside the partition itself, and depends on the support of such names within this partition type. All partitions types described above (EXT, BTRFS, FAT, SWAP, etc…) have support for some naming, with different counstraints : case sensitivity, label lenght, etc… This label can be created independently of partitition table type (GPT, MBR, APM, etc…), as it is part of the partition itself. As this label is partition-type specific, we can set/modify it while creating the partition, or with different commands (
e2label
for extX,swaplabel
for swap etc… - Name/PARTLABEL : Only for GPT disks, unsupported on MBR disks. This name is handled at partition table level, therefore it can be set with partitioning tools such as
parted(8)
,sgdisk(8)
, etc…
Practical use ot these names is mostly in /etc/fstab
and for the automounter
. As you may know, it is badtm to use physical device names (such as /dev/sda1
) as reference, as nothing guarantees they will keep the same name between reboots. We can use instead four different naming schemes : UUID
, PARTUUID
, LABEL
, and PARTLABEL
.
I originally switched from UUID/PARTUID (long and impossible to remember) to PARTLABEL, but now I simply use LABEL, reserving PARTLABEL for more descriptive information, such as "disk 2 root partition".
Example:
LABEL=foo /foo ext4 defaults 0 2 # GPT or MBR
PARTLABEL=exp /export ext4 defaults 0 2 # Only on GPT
As a sidenote, the blkid(8)
and lsblk(8)
commands can show all above information. Here with blkid :
$ blkid /dev/sdb*
/dev/sdb1: LABEL="EFI1" UUID="25E3-E3C5" TYPE="vfat" PARTLABEL="efi1" PARTUUID="9ce00989-9e23-4866-8c3b-8926604505a0"
/dev/sdb2: LABEL="root1" UUID="13243a74-031c-41b5-8a72-6c03d339e857" TYPE="ext4" PARTLABEL="root1" PARTUUID="6b32e47d-faaa-4fb6-8766-601e471e2305"
/dev/sdb3: LABEL="swap1" UUID="e9aa408c-33a4-41d9-aec3-afca45e5f9bd" TYPE="swap" PARTLABEL="swap1" PARTUUID="8ab9aba3-9331-4e4b-94e7-62ab22f9aeed"
/dev/sdb4: LABEL="export1" UUID="85b3c324-d374-4718-afd9-f6110eb2fcfa" TYPE="ext4" PARTLABEL="export1" PARTUUID="4ab6583a-9417-4c8c-a4bb-10770820471c"
DONE partition table creation
We will create here a GPT disk able to boot both with BIOS and UEFI methods.
The sizes used will be, for my 500Gb disk:
# | Partition | Partition type | FS type | Size |
---|---|---|---|---|
- | protective MBR | - | - | Whole disk |
1 | BIOS boot | 0xef02 - BIOS boot | - | 1 MiB |
2 | UEFI boot | 0xef00 - EFI boot | fat | 100 MiB |
3 | / | 0x8300 - Linux filesystem | ext4 | 30 GiB |
4 | swap | 0x8200 - Linux swap | swap | 4 GiB |
5 | export | 0x8300 - Linux filesystem | ext4 | Remaining |
Note : to get a summary of partition types, you may use sgdisk -L
.
Method 1: General case
Many commands can create a partition table. Here, I will create a GPT
scheme, with sgdisk(8)
:
$ sudo sgdisk --clear \
--new 1::+1M --typecode=1:ef02 --change-name=1:'BIOS boot partition' \
--new 2::+100M --typecode=2:ef00 --change-name=2:'EFI System' \
--new 3::+30G --typecode=3:8300 --change-name=3:'Linux root partition' \
--new 4::+4G --typecode=4:8200 --change-name=4:'Linux swap' \
--new 5::-0 --typecode=5:8300 --change-name=5:'Linux export partition' \
${DST}
Source : gpt bios efi boot
Method 2: Duplicate existing partition table
If ${SRC}
and ${DST}
disks are identical, and if you wish to keep exactly the same partitioning, you can (1) replicate the partition table (2) generate new random UIDs for the new disk.
$ sudo sgdisk ${SRC} --replicate=${DST}
The operation has completed successfully.
$ sudo sgdisk --randomize-guids ${DST}
The operation has completed successfully.
Basic partitions check
We can use lsblk(8)
(or blkid(8) as above) to check the new disk situation :
$ sudo lsblk -l -o NAME,SIZE,PARTLABEL,PARTUUID,FSTYPE,LABEL ${DST}
NAME SIZE PARTLABEL PARTUUID FSTYPE LABEL
sda 465.8G
sda1 1M BIOS boot partition 59e81b6b-26cf-40b3-ba8e-2d0366b52a6b ext4 root2
sda2 100M EFI System 843d4f41-d774-44fb-9285-ba310786720a
sda3 30G Linux root partition e14f484f-337a-44f2-8775-dcbe45a69b1f
sda4 4G Linux swap c1bc4b83-2861-4bb8-93de-e1279c58b58c
sda5 431.7G Linux export partition 41397dd5-6e98-4fdb-82af-c0da1afccbb1
We can note that the two last columns (file system type and file system label) are still meaningless for destination disk, as we did not initialize them.
For instance, sda1
contains information about a previous ext4 filesystem. We can wipe this with a dd(1)
, or wipefs(8)
:
$ sudo wipefs /dev/sda1
DEVICE OFFSET TYPE UUID LABEL
sda1 0x438 ext4 38fcc5a5-2faf-4fa3-a8b7-512007938938 root2
$ sudo wipefs --all /dev/sda1
/dev/sda1: 2 bytes were erased at offset 0x00000438 (ext4): 53 ef
$ sudo lsblk -l -o NAME,SIZE,PARTLABEL,PARTUUID,FSTYPE,LABEL ${DST}
NAME SIZE PARTLABEL PARTUUID FSTYPE LABEL
sda 465.8G
sda1 1M BIOS boot partition 59e81b6b-26cf-40b3-ba8e-2d0366b52a6b
sda2 100M EFI System 843d4f41-d774-44fb-9285-ba310786720a
sda3 30G Linux root partition e14f484f-337a-44f2-8775-dcbe45a69b1f
sda4 4G Linux swap c1bc4b83-2861-4bb8-93de-e1279c58b58c
sda5 431.7G Linux export partition 41397dd5-6e98-4fdb-82af-c0da1afccbb1
Voilà !
DONE Filesystems creation
If you remember, we will format DST partitions as following:
Part # | usage / mount point | FS type | Label/FS name (for fstab) |
---|---|---|---|
${DST}1 | BIOS boot | - | - |
${DST}2 | EFI boot | fat | EFI2 |
${DST}3 | / | ext4 | root2 |
${DST}4 | swap | swap | swap2 |
${DST}5 | /export | ext4 | export2 |
Let's do it:
$ sudo mkfs.vfat -n EFI2 ${DST}2
mkfs.fat 4.1 (2017-01-24)
$ sudo mkfs.ext4 -L root2 ${DST}3
mke2fs 1.45.5 (07-Jan-2020)
Creating filesystem with 7864320 4k blocks and 1966080 inodes
Filesystem UUID: 3e921e46-19a7-4c3f-b72a-0169e573bc4f
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
$ sudo mkswap -L swap2 ${DST}4
Setting up swapspace version 1, size = 4 GiB (4294963200 bytes)
LABEL=swap2, UUID=6613ebaf-3000-46ee-917f-c577054b5861
$ sudo mkfs.ext4 -L export2 ${DST}5
mke2fs 1.45.5 (07-Jan-2020)
Creating filesystem with 113157633 4k blocks and 28295168 inodes
Filesystem UUID: fa670dfa-0a24-4c58-8013-f55a9cca6383
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
102400000
Allocating group tables: done
Writing inode tables: done
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done
Here is the final result :
$ sudo lsblk -l -o NAME,SIZE,FSTYPE,LABEL,PARTLABEL,UUID,PARTUUID ${DST}
NAME SIZE FSTYPE LABEL PARTLABEL UUID PARTUUID
sda 465.8G
sda1 1M ext4 root2 BIOS boot partition 38fcc5a5-2faf-4fa3-a8b7-512007938938 59e81b6b-26cf-40b3-ba8e-2d0366b52a6b
sda2 100M vfat EFI2 EFI System 67DC-2F82 843d4f41-d774-44fb-9285-ba310786720a
sda3 30G ext4 root2 Linux root partition 3e921e46-19a7-4c3f-b72a-0169e573bc4f e14f484f-337a-44f2-8775-dcbe45a69b1f
sda4 4G swap swap2 Linux swap 6613ebaf-3000-46ee-917f-c577054b5861 c1bc4b83-2861-4bb8-93de-e1279c58b58c
sda5 431.7G ext4 export2 Linux export partition b4db512d-02fb-448f-af07-64c4ede7e135 41397dd5-6e98-4fdb-82af-c0da1afccbb1
DONE /etc/fstab preparation
/etc/fstab
will differ between SRC and DST. I prefer to keep both versions on all disks, and copy the correct one as running version later.
First, I prepare it with the following changes :
[...]
LABEL=EFI1 /boot/efi.old vfat noauto,defaults 0 2
LABEL=root1 / ext4 errors=remount-ro 0 1
LABEL=export1 /export ext4 defaults 0 2
LABEL=swap1 none swap nofail,sw,pri=0 0 0
[...]
I create a new section for disk2, and merge the swap information (better to use all available swap).
[...]
######################## When booting on disk1
LABEL=EFI1 /boot/efi.old vfat noauto,defaults 0 2
LABEL=root1 / ext4 errors=remount-ro 0 1
LABEL=export1 /export ext4 defaults 0 2
######################## When booting on disk2
#LABEL=EFI2 /boot/efi.old vfat noauto,defaults 0 2
#LABEL=root2 / ext4 errors=remount-ro 0 1
#LABEL=export2 /export ext4 defaults 0 2
######################################## swap
LABEL=swap1 none swap nofail,sw,pri=0 0 0
LABEL=swap2 none swap nofail,sw,pri=0 0 0
[...]
We copy the fstab file to two versions: fstab.disk1
and fstab.disk2
:
$ sudo cp -p /etc/fstab /etc/fstab.disk1
$ sudo cp -p /etc/fstab /etc/fstab.disk2
And now, we immediately fix the version for the new disk, commenting-out the disk1 part, and uncommenting the disk2 one :
[...]
######################## When booting on disk1
#LABEL=EFI1 /boot/efi.old vfat noauto,defaults 0 2
#LABEL=root1 / ext4 errors=remount-ro 0 1
#LABEL=export1 /export ext4 defaults 0 2
######################## When booting on disk2
LABEL=EFI2 /boot/efi.old vfat noauto,defaults 0 2
LABEL=root2 / ext4 errors=remount-ro 0 1
LABEL=export2 /export ext4 defaults 0 2
######################################## swap
LABEL=swap1 none swap nofail,sw,pri=0 0 0
LABEL=swap2 none swap nofail,sw,pri=0 0 0
[...]
Step 2 : First copies
Double-double check everything
GNU/Linux (to be more precise: the magic comes from udev(7)
) offers a very convenient naming system for devices. I mean : partitions can be directly be accessed by their name, UUID, etc…
If, like me, you prefer to base your naming on partition labels, you can just have a look in /dev/disk/by-label
:
$ ls -l /dev/disk/by-label
total 0
lrwxrwxrwx 1 root root 10 Oct 12 22:07 EFI1 -> ../../sdb1
lrwxrwxrwx 1 root root 10 Oct 12 22:07 EFI2 -> ../../sda2
lrwxrwxrwx 1 root root 10 Oct 12 22:07 export1 -> ../../sdb4
lrwxrwxrwx 1 root root 10 Oct 12 22:07 export2 -> ../../sda5
lrwxrwxrwx 1 root root 10 Oct 12 22:07 porsche1 -> ../../sdc1
lrwxrwxrwx 1 root root 10 Oct 12 22:07 root1 -> ../../sdb2
lrwxrwxrwx 1 root root 10 Oct 12 22:07 root2 -> ../../sda3
lrwxrwxrwx 1 root root 10 Oct 12 22:07 root3 -> ../../sdd1
lrwxrwxrwx 1 root root 10 Oct 12 22:07 swap1 -> ../../sdb3
lrwxrwxrwx 1 root root 10 Oct 12 22:07 swap2 -> ../../sda4
Please ensure that these labels correspond to what you expect, and point to the correct devices.
Do the filesystems copies
We will mount the new disk partitions we wish to duplicate (in my case: the root and export partitions) in /mnt
.
First ensure that in the disk labels mapping table above, the partitions labels correctly
DONE Mount destination filesystems (automounter version)
With a correct automount system, such as explained above, we just create symlinks in mnt to the corresponding /labels in /dev/hd
. This is not necessary, but it is simpler to access directly /mnt/export2/
instead of /mnt/hd/export2/
.
$ cd /mnt/
$ for d in root export EFI; do ln -s hd/${i}2; done
DONE Mount destination filesystems (manual version)
We need to create the same directories than above in ,/mnt and mount the corresponding filesystems :
$ cd /mnt
$ sudo mkdir root2 export2 EFI2
$ sudo mount -t ext4 LABEL=root2 /mnt/root2
$ sudo mount -t ext4 LABEL=export2 /mnt/export2
I do not mount EFI2 partition here, it is useless until migration to UEFI boot is complete.
DONE Last checks
We double check that all mount points are correct. We should have something like :
$ ls /mnt/{root,export}2
/mnt/export2:
lost+found
/mnt/root2:
lost+found
$ mount -l | grep $DST
/dev/sda5 on /mnt/hd/export2 type ext4 (rw,relatime) [export2]
/dev/sda3 on /mnt/hd/root2 type ext4 (rw,relatime) [root2]
The copies
We eventually make the copies. As it will be long, I run them in background and send me an email. You may prefer to send the output to a file, for instance if your email system is not configured. Or run the commands in different terminals.
TAKE CARE DESTINATION DIRECTORIES : Use your own names/mount points.
$ sudo rsync -axH / /mnt/root2/ 2>&1 | mail -s "copy root" me@domain.com
$ sudo rsync -axH /export/ /mnt/export2/ 2>&1 | mail -s "copy export" me@domain.com
Note : The option -x
above (same as --one-file-system
) prevents disallow crossing source filesystems boundaries. -H
preserves hard links. If you use ACLs, or extended attributes, you may consider -A
and/or -X
options.
If you have the automount setup, you may find easier to use something like that, if you have many partitions :
$ for i in root export; do
> sudo rsync -axH /mnt/${i}1/ /mnt/${i}2/ 2>&1 | mail -s "copy ${i}" me@domain.com &
> done
basic checks
When copies are done, you can roughly check that DST partitions occupation is similar to SRC :
$ df -h / /mnt/root2
Filesystem Size Used Avail Use% Mounted on
/dev/sdb2 20G 17G 1.8G 91% /
/dev/sda3 30G 17G 12G 61% /mnt/hd/root2
$ df -h /export /mnt/export2
Filesystem Size Used Avail Use% Mounted on
/dev/sdb4 434G 165G 247G 41% /export
/dev/sda5 424G 166G 237G 42% /mnt/hd/export2
Reminder table
I strongly suggest you write down a table such as this one, as a reminder :
Description | SRC Label | DST Label | SRC dir | DST dir |
---|---|---|---|---|
root partition | root1 | root2 | / | /mnt/root2 |
export partition | export1 | export2 | /export | /mnt/export2 |
EFI partition | EFI1 | EFI2 | /boot/efi.old | /mnt/EFI2 |
Step 3 : Destination disk setup after initial copy
We will need some changes on destination disks. This includes fstab adjustment, crontabs (if they contain specific device references), grub, etc…
DONE fstab fix
We simply copy the fstab we prepared earlier I suggested above :
$ sudo cp /mnt/root2/fstab.disk2 /mnt/root2/fstab
DONE Make the new disk bootable
I suggest to read some information about chroot(8)
and grub
before going further.
I will mount the new filesystems to a full subtree so that we can create a bootable disk on it. Note that if you have a separate boot
or usr
partition, they must be mounted relatively to new root mount-point. Nothing below should be done if you do not understand the commands. NOTHING.
$ sudo mkdir /mnt/disk2
$ sudo mount LABEL=root2 /mnt/disk2
# if you have separate boot (or /usr) partition do something similar to :
$ sudo mount LABEL=boot2 /mnt/disk2/boot
Now, we bind-mount the virtual filesystems from the running system :
$ sudo mount -o bind /sys /mnt/disk2/sys
$ sudo mount -o bind /proc /mnt/disk2/proc
$ sudo mount -o bind /dev /mnt/disk2/dev
We can now change our root reference with the new disk partitions. I strongly suggest to use a terminal dedicated for that :
$ cd /mnt/disk2
$ sudo chroot .
# update-grub
Sourcing file `/etc/default/grub'
Sourcing file `/etc/default/grub.d/init-select.cfg'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-5.4.0-48-generic
Found initrd image: /boot/initrd.img-5.4.0-48-generic
Found linux image: /boot/vmlinuz-5.4.0-47-generic
Found initrd image: /boot/initrd.img-5.4.0-47-generic
Found linux image: /boot/vmlinuz-5.4.0-45-generic
Found initrd image: /boot/initrd.img-5.4.0-45-generic
Found memtest86+ image: /boot/memtest86+.elf
Found memtest86+ image: /boot/memtest86+.bin
Found Ubuntu 20.04.1 LTS (20.04) on /dev/sdb2
done
You probably noticed the last line: A boot system was found on /dev/sdb2, which was the original disk.
Let's install grub now, to finish the procedure :
# grub-install ${DST}
Installing for i386-pc platform.
Installation finished. No error reported.
Testing
You can now reboot your system, and change BIOS settings to boot from new disk. This step is necessary (choosing second disk kernel from first disk grub menu is not sufficient, as it does not confirm grub is correct on second disk).
Some problems with what was done
There are a couple of issues with what we did.
fstab
As fstab is part of new disk, we can either :
- Prevent it to be copied, in rsync rules
- Copy the correct fstab on target disk after the rsync. I prefer the latter method.
Useless dirs
We may want to avoid copying some directories, possibly huge: log files, cache files, virtual file systems, etc…
Databases
Obviously, copying a database while it is live is a badTM idea. Again, we have three solutions :
- Stop it on source disk before the copy, if your system allows it.
-
Separate the database directory sync from the container filesystem one, to allow a shorter database downtime. Something like :
- copy container filesystem without database directory
- stop database
- copy database directory
- restart database
- Make a database dump, and include them in rsync, while excluding database directory itself. The difficulty here is to reload the dumps on target disk : If we do it after the copy, it probably means we need a second database instance. If we delay this restore operation, for instance to do it when we boot on target disk, there are risks we simply forget, and we need some changes for database server startup, that should work also on source disk, as next copies will also include this configuration.
Solutions 2 and 3 look messy, and are prone to errors.
My preference is for first solution (DB server shutdown before sync, and restart after the copy is done).
Mariadb
*TODO *: For mariadb/mysql, check importance of innodb_fast_shutdown global variable (should it be 0 or 1 ?)
As explained above, I prefer to stop/restart database : as it is a desktop, database uptime is not really a priority for me.
$ sudo systemctl stop mariadb
$ sudo systemctl start mariadb
TESTING script to ease subsequent copies
Below is a tentative bash script, which will automate the processus after the first copy. We also assume that automounter is configured as explained above. And finally, it is assumed that ,/boot is not a separate partition, and that source disk have same structure (1 partition on source disk corresponds to 1 partition on destination disk), and that "/" partitions labels are "rootX", such as root1, or root2.
TESTING The bash script
#!/bin/bash
#
# dup-boot-disk.sh - duplicate live system partitions and install grub on new disk.
#
# This script will not work for all situations, I strongly suggest you don't use it
# if you don't fully understand it.
# dest device (used for grub)
DSTDISK=/dev/sda
# partitions suffixes, for source and destination partitions.
# For example, if we want to copy XX partition, source partition will be
# /mnt/XX${SRCNUM}, and destination will be /mnt/XX${DSTNUM}
SRCNUM=2
DSTNUM=1
# array of partitions to copy
TO_COPY=(root export)
# An alternative to SRCNUM, DSTNUM, and TO_COPY variables would be to have
# an array containing src and destination partitions:
# (partsrc1 partdst1 partsrc2 partdst2 etc...)
# example:
# TO_COPY=(root2 root1 export2 export1)
# declare -i i
# for ((i=0; i<${#TO_COPY[@]}; i+=2)); do
# SRC=${#TO_COPY[$i]}
# DST=${#TO_COPY[$i + 1]}
# etc...
# where we will configure/install grub: mount point, device
GRUB_ROOT=/mnt/root${DSTNUM}
GRUB_DEV=/dev/$(lsblk -no pkname /dev/disk/by-label/root${DSTNUM})
# we will use ".rsync-disk-copy" files to exclude files/dirs
RSYNCOPTS=(-axH --delete)
FILTER="--filter=dir-merge .rsync-disk-copy"
# stop what could be problematic (databases, etc...)
echo systemctl stop mariadb
# partitions copy
for part in ${TO_COPY[@]}; do
SRCPART=/mnt/${part}${SRCNUM}
DSTPART=/mnt/${part}${DSTNUM}
echo copy from $SRCPART to $DSTPART
echo sudo rsync ${RSYNCOPTS} "$FILTER" "$SRCPART" "$DSTPART"
done
# grub install
# mount virtual devices
echo mount -o bind /sys ${GRUB_ROOT}/sys
echo mount -o bind /proc ${GRUB_ROOT}/proc
echo mount -o bind /dev ${GRUB_ROOT}/dev
echo chroot ${GRUB_ROOT} update-grub
echo chroot ${GRUB_ROOT} grub-install ${GRUB_DEV}
# restart stopped process (db, etc...)
echo systemctl start mariadb
DONE rsync filter files
An example of files that can be excluded. Note the last line, /mnt/*/
: It is linked to automounter, to avoid having all directories to be mounted ruring rsync copy.
- /dev
- /media
- /lost+found
- /proc
- /run
- /srv
- /sys
- /tmp
- core
- *~
- #*#
- /etc/fstab
- /mnt/*/
Links
- grub
- [Stack Exchange] : How to install GRUB on a new drive ?
- [Ask Ubuntu] : How to manually install grub with two hard disks
- [Linux Mint Forums] : Linux Mint Fresh Install on Second DISK
- [TLDP] Hard Disk Upgrade Mini How-To
- UEFI
- Switch Debian from legacy to UEFI boot mode
- How To Copy a GPT Partition Table to Another Disk using sgdisk
- Others