Moving an existing BackupPC partition to LVM

I recently discussed how to use LVM to make a live copy of a BackupPC pool. That guide covers how to set up LVM on a new server with no data. But what if you already have a working BackupPC install, and you want to move your existing pool to LVM?

I wrote a post 18 months ago on moving a Linux root partition to software RAID. Moving to LVM shares a lot of the same problems. The differences are:

  • We’re not touching the root (or /boot) filesystem. Therefore we don’t have to deal with BIOS or GRUB. In fact, this entire process can be done without rebooting.
  • We must copy the filesystem bit-for-bit instead of using file-aware tools like rsync, cp, or tar. This is due to the design of the BackupPC storage pool.

If you have another disk to use as a swap space, this problem is easy to solve. Simply unmount your backuppc partition and use dd to clone the data to the new drive. Then make create a logical volume on the old partition and copy it back.

If you don’t have another disk, you must convert the disk layout in place. The instructions in this post can be used to move any filesystem to LVM; just disregard the parts specific to BackupPC. I assume that your server’s drive and filesystem layout is as described in my last post:

  • /dev/sda provides the root filesystem (and possibly others). We’re not concerned about it here.
  • /dev/sdb is a RAID array with one partition. It does not yet use LVM, and contains an ext3 filesystem mounted on /var/lib/backuppc.

The general plan is:

  1. Reduce filesystem usage on /var/lib/backuppc to 49% or less
  2. Resize the filesystem and partition to 49%
  3. Create a second partition and logical volume with the free space
  4. Copy the filesystem to the logical volume
  5. Add the first partition to the logical volume
  6. Expand the logical volume and filesystem
  7. Mount the logical volume

Warning: this is a multi-day process with many long delays for large data transfers. I’ll identify the delays as we go along. Never interrupt a filesystem operation unless you know exactly what you are doing. I recommend executing all long-running processes in a GNU screen session in case your network connection is interrupted.

Step 1: Reduce filesystem usage on /var/lib/backuppc to 49% or less

If your BackupPC pool filesystem is larger than 49%, you must reduce disk usage.

  1. Remove old backups. Reduce the BackupPC variables $Conf{FullKeepCnt} and $Conf{IncrKeepCnt} to keep fewer backups. Alternately, use a script like to delete old ones manually.
  2. Set $Conf{BackupPCNightlyPeriod} = 1
  3. Wait for the next BackupPC_Nightly run. BackupPC will purge old backups and clean the pool of unnecessary files. This may take several days, but during this time you can perform backups as normal.

Do not proceed until filesystem usage is below 49%. Verify this with df.

Step 2: Resize the filesystem and partition to 49%

You may wish to read this guide first. It is possible to use the parted “resize” function for this, but it doesn’t work for all filesystems. We’ll do it manually.

  1. Stop BackupPC. From this point no new backups will be kept until the entire process is finished, which may be several days.
    /etc/init.d/backuppc stop
  2. Unmount the partition
    umount /var/lib/backuppc
  3. Get the block size and block count for the partition, and calculate the new size in K.
    tune2fs -l /dev/sdb1 | grep Block

    I’ve used a 10 GB partition in this example, and we now know that this filesystem consists of 2620595 blocks of 4K size. Calculate the new size as follows:

    new size = (current size in blocks) * (block size in K) * 0.49

    Round this number down. Our new size will be 2620595 * 4 * 0.49 = 5136366 K.

  4. Check the filesystem. This may take several hours or days.
    fsck -f /dev/sdb1
  5. Resize the filesystem.
    resize2fs /dev/sdb1 5136366K

  6. Resize the partition by deleting it and creating a new one of 5136366K.
    fdisk /dev/sdb

Step 3: Create a second partition and logical volume with the free space

  1. If you haven’t already, install LVM.
    apt-get install lvm2
  2. Use fdisk again to create a second partition. You can combine this step with the last one. Hit enter at the prompts to use all available space, and change the new partition to type 8e, Linux LVM.
    fdisk /dev/sdb
  3. The second partition must be equal to or larger than the first partition. If it is not, check your math or try resizing to a smaller percentage and try again.
  4. Create a volume group and logical volume.
    pvcreate /dev/sdb2
    vgcreate volgroup /dev/sdb2
    lvcreate -l 100%VG -n backuppc volgroup

Step 4: Copy the filesystem to the logical volume

  1. Use dd, the sysadmin’s oldest friend, to copy the filesystem bit-for-bit from the old partition to the new. This may take several hours or days.
    dd if=/dev/sdb1 of=/dev/volgroup/backuppc
  2. If you get impatient during the copy, you can force dd to output the current progress. Find the process ID of the running dd, and send a USR1 signal.
    ps -ef | grep dd

    kill -USR1 4786
  3. A neater way to do this is:
    dd if=/dev/sdb1 of=/dev/volgroup/backuppc &
    kill -USR1 $pid
  4. When dd finishes you will see one last progress report which should total the size of /dev/sdb1.

Step 5: Add the first partition to the logical volume

We’ve copied our data to the logical volume, so we no longer need the original partition. Add it to the volume group.

  1. Use fdisk to change partition 1 to type 8e.
    fdisk /dev/sdb
  2. Add it to the volume group.
    pvcreate /dev/sdb1
    vgextend volgroup /dev/sdb1

Step 6: Expand the logical volume and filesystem

We’ve moved the data to a logical volume and added all the disk space to the volume group. Now we expand the logical volume and filesystem to use the available space.

  1. Extend the logical volume. I advise using only 75% so you can use LVM snapshots.
    lvextend -l 75%VG /dev/volgroup/backuppc
  2. Resize the filesystem to use the entire logical volume. This may take several hours or days.
    resize2fs /dev/volgroup/backuppc
  3. If at any point you’ve mounted the filesystem, you’ll have to run a new fsck before resizing. This will cause another long delay, so try to avoid it.

Step 7: Mount the logical volume

We’re now ready to mount the new logical volume on /var/lib/backuppc and restart BackupPC.

  1. Edit /etc/fstab and replace any references to /dev/sdb1 with /dev/volgroup/backuppc. If your fstab uses UUIDs instead of explicit device paths, you shouldn’t have to change anything.
  2. Mount the filesystem.
    mount /var/lib/backuppc
  3. Restart BackupPC
    /etc/init.d/backuppc start

You have successfully migrated to LVM!

Tags: , ,

  1. Philip’s avatar

    I’m going to confess to a horrible thing I did.

    I converted to LVM. In place. Without first reducing disk usage to 49%. It was a bit of a Zen exercise, and the data wasn’t at all valuable, obviously, because no one should expect that sort of thing to work.

    1. a script to reverse a block device in 4M chunks (included with linux kernel documentation, needs adjustment)
    2. dd
    3. cp –sparse=always
    4. losetup

    I’m sure you can figure out the process (free last extent of disk. set up reversed lvm device at end of disk. copy the last extent of the file system into the reversed lvm device, then grow the reversed lvm device at its logical end, and its physical beginning. repeat. set up a normal lvm device at the start of the disk, migrating extents there from the reversed lvm device, which will shrink to zero and leave you, ideally, with something you could have done in ten minutes with a backup disk.

    The cp –sparse=always comes in if you have to use intermediate file systems because a file system was unshrinkable – you can still effectively shrink it by separating it out into chunks stored in sparse files in another file system and keeping it as zeroed-out as possible.

    Forgive me father, for I have sinned.


    1. tyler’s avatar

      That is insane. It would work, though. :)


    2. Philip’s avatar

      It did work, though it was sheer dumb luck that none of the many, many dd invocations went horribly wrong.

      But, seriously: why is this so difficult? My impression is ext[234]fs is pretty good at not using certain areas of the disk (historically, for badblocks). In theory, it should be as easy as this:
      0. unmount as file system
      1. create an extent-sized file of zeroes, and make sure it is extent-aligned: thus, you’ll have a free extent to shift things to.
      2. copy the first file system extent into the zeroed extent
      3. mark the duplicate extent as bad, preventing the file system from using it. delete the zeroes file.
      4. overwrite the first file system extent with LVM header data, remapping the first extent to the new copy, the other extents 1:1, and the bad extent to an error/zeroes entry
      5. mount as LVM

      That would take 10 seconds, it would reduce your file system size by one extent (though you could always resize2fs and use the last extent to avoid the 4M marked as bad artifact), and it would actually be safer than anything that involves rewriting everything twice (obviously, any file system-modifying code should be considered extremely dangerous for the first few hundred times you run it). Also note that it splits nicely into stuff that only involves the file system and a pure LVM operation that turns a partition into an LVM pv containing the partition.

      I’m afraid my point is that I’m a grumpy old man, and it’s 2010 and we still have exciting new features added in a way that makes migration unbelievably difficult. A file system that you want to turn into, essentially, a bunch of files on a new file system you create around it isn’t the exception, it’s the rule for any exciting migration.

      Want your file system encrypted? Essentially, you’ve got to go through the same thing again, because some more metadata wants to be stored at the beginning of the image.

      Want your LVM layout encrypted? Your best bet is LVM over crypto over LVM, so that’s the same procedure again (and another extent lost, but that’s hardly an issue).

      Sorry for ranting. It’s good to have readable instructions for the common, using-less-than-49% case, and thanks for going to the trouble of writing those.


    3. Adam’s avatar

      IMHO, LVM is just that, a couple of extra blocks at the beginning of the partition. So I would have thought it should be simple enough to:
      1) reduce the fs by a couple of blocks (say 4M or whatever)
      2) move the entire fs to the ‘end’ of the partition (move the second last block to the last block and so on until the first block is free)
      3) create the LVM headers/etc at the beginning of the partition

      All done… even if the filesystem was originially 99% full, and only one complete copy of the data. I’ve certainly managed to successfully complete the reverse of the above (recovering a filesystem from a corrupted LVM, where there was only a single filesystem on a single PV, etc… using mount -o loop …. and telling losetup to skip the first few blocks… don’t see why it couldn’t work in reverse.

      PS, if your data is important, don’t do it, it isn’t worth it :)


      1. tyler’s avatar

        I’d love to see some steps for doing this, with commands. I’m sure I’ll face this problem again.


      2. Gabriel’s avatar

        There’s an in-place approach that works for most common filesystem types (ext[2-4], btrfs, and reiserfs all of which have shrink support); the idea is to move one PE from the start to the end of the partition, and inject LVM configuration in the freed space (superblock label and PV metadata area, with an LV that puts the moved PE at the correct logical address). For filesystems that support badblocks but not shrinking, reserving contiguous data through a file and badblocking it might also work to free the PE space, but there should be some simpler approaches that work on multiple partitions: find one partition with a shrinkable filesystem or discardable swap space, and use it to generate a PV that covers multiple partitions; then edit the partition table. So far I have implemented the simple, single-partition approach (lvmify, link on the username).



Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">