{"id":613,"date":"2025-04-07T05:40:49","date_gmt":"2025-04-07T05:40:49","guid":{"rendered":"https:\/\/www.micha.name\/blog\/?p=613"},"modified":"2025-04-07T05:40:50","modified_gmt":"2025-04-07T05:40:50","slug":"migrate-partitions-to-lvm-on-a-live-server","status":"publish","type":"post","link":"https:\/\/www.micha.name\/blog\/2025\/04\/07\/migrate-partitions-to-lvm-on-a-live-server\/","title":{"rendered":"Migrate partitions to LVM on a live server"},"content":{"rendered":"\n<h2>Background<\/h2>\n\n\n\n<p>My server was provisioned by <a href=\"https:\/\/contabo.com\" target=\"_blank\" rel=\"noreferrer noopener\">Contabo<\/a> as a <a href=\"https:\/\/www.debian.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Debian<\/a> 9 server with a traditional MBR partition layout. At some point I did manage to at least split <code>\/var<\/code> and <code>\/home<\/code> off from the root partition, leaving the following layout for many years:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT\nsda      8:0    0   1.2T  0 disk \n\u251c\u2500sda1   8:1    0   237M  0 part \/boot\n\u251c\u2500sda2   8:2    0    20G  0 part \/\n\u251c\u2500sda3   8:3    0    20G  0 part \/var\n\u251c\u2500sda4   8:4    0     1K  0 part \n\u2514\u2500sda5   8:5    0 259.8G  0 part \/home<\/code><\/pre>\n\n\n\n<p>Recently I upgraded the VPS to have more diskspace, as I was starting to run low and, instead of mucking about with an ever-increasing amount of relatively inflexible extended partitions and symlinks, I decided to figure out a way to convert this layout to LVM which will provide me with flexibility to mange the disk space in the future.<\/p>\n\n\n\n<p>After a bit of research and prototyping all the steps in a local VM, I came up with the following procedure which worked for me.<\/p>\n\n\n\n<p>Note that I did NOT convert the <code>\/boot<\/code> partition and that the disk remains an MBR-partitioned disk.<\/p>\n\n\n\n<h2>Before Starting<\/h2>\n\n\n\n<p>It is <strong>strongly recommended<\/strong> to take a backup and\/or snapshot of the server before commencing. A singly mistype or issue during the conversion could lead to full data loss.<\/p>\n\n\n\n<h2>Initial Conversion<\/h2>\n\n\n\n<p>The first step is to convert <code>sda2<\/code> and <code>sda3<\/code> to use the Logical Volume Manager (LVM) and move the root and var partitions into that. This requires a multi-step process:<\/p>\n\n\n\n<ol><li>Create a temporary LVM Physical Volume (PV) and move the data into it<\/li><li>Update the system configuration<\/li><li>Reboot the system<\/li><li>Remove the original partitions and replace them with a new PV<\/li><li>Add the PV to the LVM Volume Group (VG)<\/li><li>Remove the temporary PV from the VG<\/li><\/ol>\n\n\n\n<p>To create the temporary PV, I first needed to increase the size of the extended partition to make use of the new disk space. I used <code>cfdisk<\/code> for this. Note that there seems to be a bug in the Debian 11 version of cfdisk &#8211; when first increasing the size of the extended partition, a message is shown stating the maximum size, but nothing happens. Deleting the size and pressing enter again applies the size last specified.<\/p>\n\n\n\n<p>Next is creating the LVM:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pvcreate \/dev\/sda6\nvgcreate vg1 \/dev\/sda6\nlvcreate -n root -L 8G vg1 &amp;&amp; mkfs.ext4 \/dev\/mapper\/vg1-root\nlvcreate -n var -L 20G vg1 &amp;&amp; mkfs.ext4 \/dev\/mapper\/vg1-var<\/code><\/pre>\n\n\n\n<p>Now we can copy the data. Note that when copying <code>\/var<\/code>, it is useful to shut down as many services on the server as possible to reduce the data being actively written to the partition. It may be a good idea to run the sync again just prior reboot.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mount \/dev\/mapper\/vg1-root \/mnt<br>rsync -avxq \/ \/mnt\/<br>mount \/dev\/mapper\/vg1-var \/mnt\/var<br>rsync -avxq \/var\/ \/mnt\/var\/<\/code><\/pre>\n\n\n\n<p>And finally we edit the system configuration:<\/p>\n\n\n\n<ul><li>vim \/etc\/fstab<ul><li>point the \/ and \/var mounts to the new LVM volumes<\/li><\/ul><\/li><li>vim \/boot\/grub\/grub.cfg<ul><li>ensure kernel command line is updated to have &#8220;root=\/dev\/mapper\/vg1-root&#8221;<\/li><\/ul><\/li><\/ul>\n\n\n\n<p><strong>fstab:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/ was on \/dev\/sda2 during installation\n#UUID=904300f1-5d90-4c10-908a-b8ac334bd021 \/               ext4    errors=remount-ro 0       0\n<strong>\/dev\/mapper\/vg1-root<\/strong>                      \/               ext4    errors=remount-ro 0       0\n# \/boot was on \/dev\/sda1 during installation\nUUID=9d8415e3-5d47-42e5-b169-ab0f5db14645 \/boot           ext4    defaults,noatime        0       1\n\n#UUID=8de1d736-5b9e-44b1-ba6f-34984912889e \/var            ext4    errors=remount-ro 0       1\n<strong>\/dev\/mapper\/vg1-var<\/strong>                       \/var            ext4    errors=remount-ro 0       1\nUUID=70554039-342d-4035-8182-ece5b032ec5b \/home           ext4    errors=remount-ro 0       1<\/code><\/pre>\n\n\n\n<p><strong>grub.cfg:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>### BEGIN \/etc\/grub.d\/10_linux ###\nfunction gfxmode {\n        set gfxpayload=\"${1}\"\n}\nset linux_gfx_mode=\nexport linux_gfx_mode\nmenuentry 'Debian GNU\/Linux' --class debian --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-a5511b28-0df7-48c2-8565-baeaede58cfa' {\n        load_video\n        insmod gzio\n        if &#91; x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi\n        insmod part_msdos\n        insmod ext2\n<strong>        insmod lvm<\/strong>\n        set root='hd0,msdos1'\n        if &#91; x$feature_platform_search_hint = xy ]; then\n          search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1  9d8415e3-5d47-42e5-b169-ab0f5db14645\n        else\n          search --no-floppy --fs-uuid --set=root 9d8415e3-5d47-42e5-b169-ab0f5db14645\n        fi\n        echo    'Loading Linux 6.1.0-0.deb11.21-amd64 ...'\n        linux   \/vmlinuz-6.1.0-0.deb11.21-amd64 <strong>root=\/dev\/mapper\/vg1-root<\/strong> ro rootdelay=10 net.ifnames=0 ixgbe.allow_unsupported_sfp=1 quiet \n        echo    'Loading initial ramdisk ...'\n        initrd  \/initrd.img-6.1.0-0.deb11.21-amd64\n}\n<\/code><\/pre>\n\n\n\n<p>Now for the scary part &#8211; rebooting. Double-check that all configurations have also been applied to the new partitions and everything has been copied correctly. <strong>Make sure<\/strong> you have a way to recover should the system not reboot!<\/p>\n\n\n\n<h2>Completing the initial conversion<\/h2>\n\n\n\n<p>Assuming the reboot went well, the system should now be running on the new LVM Logical Volumes (LV&#8217;s).<\/p>\n\n\n\n<p>Now delete the <code>\/dev\/sda2<\/code> and <code>\/dev\/sda3<\/code> partitions, and create a new LVM PV in their place. Mount it and add it to the volume group, then remove the temporary PV:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>vgextend vg1 \/dev\/sda2<br>pvmove \/dev\/sda6<br>vgreduce vg1 \/dev\/sda6<br>pvremove \/dev\/sda6<\/code><\/pre>\n\n\n\n<p>Now we can delete the temporary <code>\/dev\/sda6<\/code> partition.<\/p>\n\n\n\n<h2>Moving the data<\/h2>\n\n\n\n<p>Next we have to move the home partition. Again, this is a multi-step process as we first have to move the data out of the extended partition, then remove the extended partition.<\/p>\n\n\n\n<p>First create a new primary partition, <code>\/dev\/sda3<\/code>, large enough to hold the data. Make it a PV and add it to <code>vg1<\/code> as before, then copy the data. Note that as before, ideally there should be no active users during the copy and any services which write into users&#8217; home directories should be shut down.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>vgextend vg1 \/dev\/sda3<br>lvcreate -n home -L 250G vg1 &amp;&amp; mkfs.ext4 \/dev\/mapper\/vg1-home<br>mount \/dev\/mapper\/vg1-home \/mnt<br>rsync -avxq \/home\/ \/mnt\/<\/code><\/pre>\n\n\n\n<p>Now remount the new partition in place of the home partition. It may be better and safer to reboot the system instead to avoid any data loss or corruption, since lazy-unmounting a filesystem can lead to all sorts of edge cases.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>umount -l \/home &amp;&amp; mount \/dev\/mapper\/vg1-home \/home<\/code><\/pre>\n\n\n\n<p>Next we delete all the logical partitions and the extended partition. The free data should now be between <code>sda2<\/code> and <code>sda3<\/code>, which lets us increase the size of <code>sda2<\/code>. We can now increase the LVM PV to make use of the new space. Once we&#8217;ve done that, we can <code>pvmove<\/code> the data from the temporary Physical Volume and remove it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pvresize \/dev\/sda2<br>pvmove \/dev\/sda3<br>vgreduce \/dev\/sda3<br>pvremove \/dev\/sda3<\/code><\/pre>\n\n\n\n<p>Finally, we can delete the <code>\/dev\/sda3<\/code> partition and add the remaining free space to the LVM Volume Group. From now on it is trivial to manage disk layout using LVM.<\/p>\n\n\n\n<h2>Final Layout<\/h2>\n\n\n\n<p>After all was said and done, the server ended up with a disk layout as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>NAME         MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT\nsda            8:0    0  1.2T  0 disk \n\u251c\u2500sda1         8:1    0  237M  0 part \/boot\n\u2514\u2500sda2         8:2    0  500G  0 part \n  \u251c\u2500vg1-root 254:0    0    8G  0 lvm  \/\n  \u251c\u2500vg1-var  254:1    0   20G  0 lvm  \/var\n  \u2514\u2500vg1-home 254:2    0  260G  0 lvm  \/home<\/code><\/pre>\n\n\n\n<p>For now I&#8217;ve left the LVM partition at 500GiB, which is double what the old disk was, and gives the various volumes plenty of room to grow.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Background My server was provisioned by Contabo as a Debian 9 server with a traditional MBR partition layout. At some point I did manage to at least split \/var and \/home off from the root partition, leaving the following layout for many years: Recently I upgraded the VPS to have more diskspace, as I was starting to run low and, instead of mucking about with an ever-increasing amount of relatively inflexible extended partitions and symlinks, I decided to figure out a way to convert this layout to LVM which will provide me with flexibility to mange the disk space in the future. After a bit of research and prototyping all the steps in a local VM, I came up with the following procedure which worked for me. Note that I did NOT convert the \/boot partition and that the disk remains an MBR-partitioned disk. Before Starting It is strongly recommended to take a backup and\/or snapshot of the server before commencing. A singly mistype or issue during the conversion could lead to full data loss. Initial Conversion The first step is to convert sda2 and sda3 to use the Logical Volume Manager (LVM) and move the root and var partitions [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[11,56,29],"tags":[30,101],"_links":{"self":[{"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/posts\/613"}],"collection":[{"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/comments?post=613"}],"version-history":[{"count":9,"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/posts\/613\/revisions"}],"predecessor-version":[{"id":622,"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/posts\/613\/revisions\/622"}],"wp:attachment":[{"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/media?parent=613"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/categories?post=613"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.micha.name\/blog\/wp-json\/wp\/v2\/tags?post=613"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}