After having done this same procedure a number of times, I have finally figured out the best way of transferring an entire Linux System from one drive to another and still have it be bootable.
Step 0: Did you format and partition the drives the way you want?
I write this down just to clarify that I’m assuming you know enough about Linux to have been able to partition and format your new drive the way you want. Furthermore, this guide assumes that your entire root file structure exists on one partition. If you are advanced enough to know how to have your filesystem span multiple partitions (e.g. one partition for /, another for /boot, another for /home, etc), then you should be able to translate these steps for your own situation.
Step 1: Mount your source and target devices.
Let’s say your current root filesystem exists on /dev/sda1
and you want to move it to /dev/sdb1
. We want to mount the filesystems in a fresh location so that we are only dealing with those two filesystems. Note also that your target filesystem should be cleanly formatted and large enough to contain all the files from the source filesystem.
Commands starting with #
should be run with root privileges.
# mkdir /mnt/source /mnt/target
# mount /dev/sda1 /mnt/source
# mount /dev/sdb1 /mnt/target
Step 2: Copy files
The following command will copy all the files from the source filesystem to the target filesystem
# rsync -av /mnt/source/ /mnt/target/
Note that the “v” option stands for “verbose” and tells rsync to print the name of every file it copies. You can remove the “v” option if you don’t want to see that output.
Step 3: Copy again (optional)
Since you are moving a live filesystem, it’s possible that a few things were changed. Usually, we don’t care about these incidental operating system changes, but just to be sure, you can run the exact same rsync command a second time.
# rsync -av /mnt/source/ /mnt/target/
Step 4: Mount System Directories and “chroot”
The following steps will allow you to step into the new filesystem just as if you had booted from it.
These commands will make the new filesystem work just like the real filesystem so that when you switch into it, the operating system isn’t confused.
# mount --bind /proc /mnt/target/proc
# mount --bind /sys /mnt/target/sys
# mount --bind /dev /mnt/target/dev
# mount --bind /run /mnt/target/run
# mount --bind /dev/pts /mnt/target/dev/pts
# mount --bind /etc/resolv.conf /mnt/target/etc/resolv.conf
This command puts you into the new filesystem just as if you had booted from it.
# chroot /mnt/target
At this point you are in your new filesystem. Note however, that the Linux kernel, modules and other operating system files are all still loaded from the real root, so you aren’t actually booted into your new system, but since this new filesystem is identical to the old one, everything should work just fine.
Step 5: Fix fstab
Get the UUID of your new filesystem.
# blkid |grep /dev/sdb1
(If your target filesystem was something other than /dev/sdb1
, use that instead.)
In the command output, you will see something like this.
/dev/sdb1: UUID="af6dcc3f-a5c7-42a2-a2b5-e94ccac3cdd9" TYPE="ext4"
You need to know both the UUID and the TYPE, but you don’t need the quotation marks. Copy them down somewhere.
Graphical editors might not work from the chroot environment, so you will do better using a console based editor like nano
or vim
# nano /etc/fstab
If you aren’t familiar with editing fstab files, you need to be careful here, but you shouldn’t be afraid. Each line in an fstab file tells the operating system which device to use for which part of the filesystem. The format is the same:
[device] [mount point] [filesystem type] [options] [dump] [pass]
(Each “column” is separated by one or more spaces or tabs, so even though I use the word “column” to refer to each item, they might not look like they are arranged into neat columns.)
One of the lines will have a simple slash in the second column, and that’s the line we want to change. It might look like this:
UUID=32322b4a-6b43-48da-a021-1f395a61bd2d / ext3 defaults,noatime,nodiratime 0 1
Replace the numbers after UUID= with the UUID numbers you got from the blkid
command above, and also replace the ext3
(or whatever is in the third column) with the TYPE you got from the blkid
command.
Save the file and exit.
Step 6: Fix initramfs
We only have two steps left. When Linux boots, in most cases, the first thing that is loaded is the kernel and the second thing that is loaded is an initial ram filesystem or initramfs. The initramfs is like a temporary operating system that helps the Linux kernel get everything set up for real. If your system doesn’t need an initramfs, you can skip this, but if you are running a system that doesn’t need one, you probably don’t need to be reading these instructions anyway!
This step is again platform specific, but on Ubuntu, the command is simple:
# update-initramfs -u
It may take a minute or two.
Step 7: Install the bootloader
Finally, to make sure your new drive is bootable, we need to install a bootloader. Most Linux systems these days are using grub or grub2 for their bootloader, and there are a lot of confusing ways out there to “fix” your bootloader or to “install” a bootloader, however, your Linux system should already know how to do this.
If you are running Ubuntu, simply type this:
# dpkg-reconfigure grub-pc
When the prompts ask you, be sure to install grub to your new device. In our example scenario, you would pick /dev/sdb
.
Once this is done, you should exit the chroot environment, reboot your computer, tell your BIOS to boot from the new drive, and enjoy running Linux from your new drive!
# exit
# reboot
Conclusion
This has taken a number of steps, but in summary, transferring to a new drive isn’t all that hard. It really just boils down to these things:
- freshly mount the source and target filesystems
- transfer all files with rsync
- create a good chroot environment and then chroot into it.
- update the fstab file and the initramfs file
- install the bootloader
The only tricky part is discovering how your specific Linux distribution handles the initramfs and bootloader installation. I’ve described how Ubuntu handles it here, but if your distribution handles it differently, put a comment below!
Alex
Hi Jeff,
I successfully transferred a GNU/Linux installation from an SSD to a NVMe following your procedure.
However, what came around wrong at first were the copied files on the destination drive using your rsync command.
From my experience, a trailing slash (/) should be added only at the end of the source directory, like this:
# rsync -av /mnt/source/ /mnt/target
Otherwise files end up at /mnt/target/src instead of /mnt/target
OK, these were my 2 cents.
Thanks again!
Jeff Mikels
Thanks for that point. However, you aren’t exactly right. According to rsync documentation, there should be no difference between the slash at the end of the destination path or not so long as the slash exists in the source path. I always include it because I use tab completion to make sure the target directory already exists as a directory. If for some reason, the target destination is a link or a file, tab completion will not add the slash and I will be alerted that something isn’t as I expect. Here’s a quick tutorial on how rsync handles slashes: https://gist.github.com/ilyar/a00eff120fb8d33785be4a4d3fb1f820. Here is a good discussion on Stack Overflow: https://stackoverflow.com/questions/31278098/slashes-and-the-rsync-command
Brett Johnson
Thank you Jeff, this is by far the most efficient, elegant, and least error-prone method of moving to a new disk I’ve found, and you’ve written the process up in a way that makes it easy to read, and also easy to bookmark as a reference for future use. Thanks again!