Skip to content

Instantly share code, notes, and snippets.

@inequation
Last active December 9, 2022 00:58
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save inequation/4bb10b645e915e8782c0 to your computer and use it in GitHub Desktop.
Save inequation/4bb10b645e915e8782c0 to your computer and use it in GitHub Desktop.
Guide to migrating a dual-boot Linux/Windows system to an SSD

Having painstakingly performed the operation of migrating dual-boot systems to SSDs (without a fresh install of any of the systems) twice in the recent days, I've decided to write the steps down in case I ever need to repeat it. It may also benefit someone else on the internet. This is the most efficient and least error-prone workflow to the best of my knowledge, and discovering it was not as easy as it may seem.

Please excuse the somewhat short-hand form of this guide; I take the liberty to assume the reader is a power-user and is at least familiar with Linux.

The scenario

Here are the specific conditions I worked under:

  • Dual-boot, Linux/Windows system.
  • MBR partition tables.
  • GRUB2 installed in MBR of the first drive (/dev/sda).
  • Windows installed on the first drive (/dev/sda).
    • Boot loader in /dev/sda1.
    • Actual Windows partition in /dev/sda2.
  • Linux installed on the second drive (/dev/sdb).
    • Swap partition in /dev/sdb1.
    • Actual ext4 Linux partition in /dev/sdb2.
  • Two bulk data NTFS partitions (for shared Linux/Windows access) after both systems (/dev/sda3, /dev/sdb3).

The bottom line is that we want to move both systems' partitions (/dev/sda1, /dev/sda2, /dev/sdb2) to the new SSD, but not the data partitions (I also want to keep the Linux swap on the HDD). The caveat is that the SSD is not large enough to contain verbatim clones of the partitions, so we'll need to shrink them.

Bonus points for putting /var on an HDD, too. The APT package cache gets ground quite a lot on a reasonably old Debian system, not to mention the logs.

Prerequisites

  • Linux live CD with recovery tools, preferably from your distro of choice.
  • CD drive usable as a boot device.
  • ntfsprogs or ntfs-3g installed on your Linux system. The latter is preferable, as it's actively maintained (at the time of writing).

Steps

The big takeaway is don't bother with Microsoft's NTFS resizing tools. They are downright unusable and incapable of performing useful partition resizing operations: they will always get stuck on some "unmovable" file – pagefile.sys, swapfile.sys, some \System Volume Information hodgepodge or simply refuse to shrink without specifying a reason (cf. events 258 and 259 for Defrag Application event in the Windows event log; #259 is useful, #258 is useless, and there comes a point when you just stop getting #259 altogether). Defrag doesn't help, and it seems that no one at Microsoft had the simple idea of deferring the resize operation until reboot, if the issue is that there are currently open files beyond the intended shrink boundary.

Believe me, I had tried hard to use them on the assumption that MS is bound to have a better NTFS implementation, since it's their internal tech. I was wrong.

Anyway, here's what I do, step by step:

  1. Make sure your Windows partition is in good health.

  2. Make enough room on the partition to accomodate the shrinkage.

  3. Run chkdsk /f c: and accept the prompt to schedule the check upon the next reboot.

  4. Reboot and let the check run.

  5. Reboot again.

  6. Make sure your Linux partition is in good health.

  7. Make enough room on the partition to accomodate the shrinkage.

  8. Run # e2fsck -f /dev/sdb2 and if it appears, accept the prompt to schedule the check upon the next reboot.

  9. If the prompt appeared, reboot and let the check run.

  10. Reboot again.

  11. Prepare the new partition table.Note: From this point onwards I will assume the SSD has taken the first disk slot in the system, making it /dev/sda, and shifting the HDDs one letter down; i.e. the Windows drive now becomes /dev/sdb, and the Linux one is now /dev/sdc.

  12. Boot into your Linux live CD. The live CD may offer to chroot into your existing Linux install; I suggest you do not do that just yet and instead just run from within the live CD system.

  13. Copy the MBR of the HDD to your SSD: # dd if=/dev/sdb of=/dev/sda bs=512 count=1. Yes, I know the partition table is probably invalid at this point, but it keeps GRUB (just in case we need its recovery mode; though we probably won't), and the drive UUID that Windows requires to boot from the SSD smoothly without requiring a reinstall.

  14. Pay heed to the current SSD partitioning rules. At the time of writing, it is recommended to leave 2048 sectors empty at the beginning of the drive (this is the default) and make sure all partitions start on an 8-sector boundary (i.e. start sector number should be divisible by 8). You may also want to leave ~10% of the SSD unallocated for overprovisioning to achieve extended SSD life.

  15. Fix the partition table – run # fdisk /dev/sda (not cfdisk, it will die with an error) and remove all the partitions but the Windows ones (/dev/sda1 for the boot loader and /dev/sda2 for the actual system).

  16. While still in fdisk, create the Linux partition.

  17. Make sure to issue the w command to write the partition table.

  18. Refresh the kernel's view of partition tables by running # partprobe.

  19. Copy the Linux partition. It is my experience that verbatim (dd) copies are difficult to work with (and simply unefficient), so unless your live CD provides partclone.ext4 (it makes a sparse copy, i.e. skips empty sectors), let's just create a new one and copy the contents.

  20. Create your new filesystem by running # mkfs.ext4 -L <your preferred label> /dev/sda3. At the time of writing, everyone recommends a block size of 4096, but if this changes, just set the block size to whatever is the recommendation of the day by tacking on -b <block size> to the command line before the device path.

  21. Make sure your old Linux partition is mounted somewhere you have access to. If it isn't, try this: # mkdir /tmp/oldfs && mount /dev/sdc3 /tmp/oldfs.

  22. Make sure your new Linux partition is mounted somewhere you have access to. If it isn't, try this: # mkdir /tmp/newfs && mount /dev/sdc3 /tmp/newfs.

  23. Copy your data by running # cp -afv /tmp/oldfs/* /tmp/newfs/. The -a switch does the magic of maintaining attributes and what not.

  24. Wait until the copy operation concludes.

  25. Fix up your new Linux installation.

  26. Get your live CD to chroot into the new Linux partition. This can be done from the menus in Debian's recovery mode.

  27. Find out the UUID of the new partition by running blkid -c /dev/null.

  28. Edit /etc/fstab: update the root partition UUID with what we've just read in the previous step and set the noatime option on it. Note: I purposefully do not put discard here. Calling TRIM upon every single discard is wasteful, it's better to do it periodically. More on that later.

  29. Also, while we're at it, fix up any other fstab entries that were set up by device path instead of UUID, e.g. the bulk data partitions.

  30. Re-install grub: # grub-install /dev/sda.

  31. Update GRUB configuration – scan for kernels etc.: # update-grub. Note that this will probably fail to detect any other OSes, but that's not a problem.

  32. Reboot to your new Linux installation. Make sure the SSD is the first boot device.

  33. Resize the Windows partition and clone it onto the SSD.

  34. It's a good idea to make a backup of it at this point. ntfsclone has got you covered for that – the image is sparse and can be further compressed. See the manual (man ntfsclone).

  35. Use # ntfsresize -n -i /dev/sdb2 to check if your Windows partition is good to go and how much you can shrink it.

  36. Use # ntfsresize -s <new size> /dev/sdb2 to shrink it. I recommend shrinking to just below the size of the target SSD partition for minimal time spent on file relocation.

  37. Copy the Windows boot loader to the SSD by using # ntfsclone --overwrite /dev/sda1 /dev/sdb1.

  38. Copy the Windows system volume to the SSD by using # ntfsclone --overwrite /dev/sda2 /dev/sdb2.

  39. Find out how much you can expand the new NTFS volume by running # ntfsresize -n -i /dev/sda2. Look for "device size".

  40. Expand the new NTFS volume by running # ntfsresize -s <new size> /dev/sda2. Substitute the number read in the previous step for <new size>. No unit suffixes are needed since the number is in bytes.

  41. Run # update-grub to re-detect all the OSes on your system.

  42. Configure your Linux system for SSD. This means regular re-trimming and I/O scheduling. There are many ways for doing that. I like putting a simple shell script in /usr/local/sbin/ssd.sh with the commands fstrim -v / and echo deadline > /sys/block/sda/queue/scheduler in it. Then call the script at boot or at will – I put a /usr/local/sbin/ssd.sh & line into /etc/rc.local.

  43. Fix up your new Windows installation.

  44. Reboot to Windows.

  45. Run the Microsoft Management Console (Windows key + R, type mmc), from the File menu add the Disk Management snap-in. Go to that snap-in by choosing it in the tree on the left.

  46. Your old Windows drive will be offline due to drive UUID conflict (it's stored in the MBR). Right-click it in the list in the bottom window and choose "Online". This will cause Windows to regenerate the UUID.

  47. Fix up any drive lettering screw-ups that may ensue.

  48. Configure your Windows system for SSD. I just installed Samsung Magician and let it set all the options up for "maximum performance". Note: Your OEM SSD monitoring software may have trouble recognizing the Linux partition. It's all right, though, nothing is broken, even if it shouts at you.

  49. Once you make sure your migrated system installations are all right and intact, you can delete the old system partitions from the HDDs. Re-run # update-grub after that to remove the bogus GRUB boot menu options. If you want to reclaim that newly freed space for your bulk data, however, you're in bad luck – you can't grow NTFS volumes to occupy space in front of them. Which means you'll need to go through the ntfsresize-ntfsclone-ntfsresize ping-poing again. Unless the system partition was equal or larger than the bulk partition, in which case you can skip one (or both) ntfsresize. But that is a story for another time.

Cheers & HTH.

@slarionoff
Copy link

In step 22 you create wrong folder. Copy-pasted from 21.

@wfsdiniz
Copy link

Is it possible to use a USB key instead of a LiveCD, in a CD-less system?

@toobaz
Copy link

toobaz commented Aug 11, 2020

Great work... but to be honest, I tried to follow this guide, but then stopped when I found some much simpler instructions on Stack Exchange, which worked great. Their limit is probably that they do not optimize the layout for the SSD (e.g. the 2048 sectors empty at the beginning), but that's not hard to do ex post with gparted.

@inequation
Copy link
Author

inequation commented Aug 11, 2020

Happy for you that the SE question helped you, but that doesn't handle the case where you need to resize partitions (NTFS in particular, it's obnoxious in that it maintains its own metadata which can easily get out of sync with the partition table) and install GRUB for dual booting. :) This guide looks complicated simply because I've broken the steps down a lot.

@inequation
Copy link
Author

Whoa, I never noticed the other two comments... Did Github only start sending notifications about gists sometime after May 2018? Sorry, @slarionoff and @wfsdiniz!

In step 22 you create wrong folder. Copy-pasted from 21.

You are correct, I'll fix that.

Is it possible to use a USB key instead of a LiveCD, in a CD-less system?

Absolutely. The idea is to boot off of something else than the hard drive.

@pereiradaniel
Copy link

My first time trying to do this, I managed to clone my HDD to SSD using Macrium Reflect. Initially, neither the CSM (Linux) or UEFI (Win10) boots would work. Then after some tinkering with the bios through Windows, I was able to get the CSM boot to load, but not the UEFI to boot into Win10, which kept saying "Insert media" on boot attempts.

I'm a bit novice to all this, so bear with me.

Could this be because the BIOS is not finding the partition where it is expected? Have I nuked some part of my Win10 boot process during the clone?

I will be re-attempting this SSD clone and install process in the next few days, and documenting it as OP did, but if anyone could slap me across the face with a board before I try again, it would be much appreciated.

@Trinsdar
Copy link

Will this work for gpt partition table?

@inequation
Copy link
Author

@Trinsdar I have no clue, it's been way too long since I've done it, and I don't know anything about GPT partition tables.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment