I recently came across a situation where I wanted to reduce the root volume size of an EC2 instance I had in place. It seems like a common request that people occasionally have, but every solution I tried did not work. Many of the solutions were dated or relied on a very specific process that may work for some situations. It became frustrating, so instead, I figured it out my own way, but I wanted to share this as a reference for others who are trying to solve this problem.

My goal was to find a generic solution that would work in a variety of situations consistently even as the years go by. Below you will see we are using XFS filesystem, but this should work with any type.

Here is a high-level look at the steps that will be involved:

  1. Take a snapshot of your current root volume for a backup in case things go wrong.
  2. Update FSTAB on the instance to not use UUID.
  3. Create a new instance using the original AMI at the volume size you want.
  4. Associate new instance volume with the pre-existing instance.
  5. Rsync files from the root volume to the new volume.
  6. Use the new smaller volume with your instance.

Now let's go through each step in more detail.

Take a Snapshot of your current root volume

Shut down your instance in EC2, and then create a snapshot of the root volume marked with / for the EC2 instance in question.

Take a snapshot of your root volume

This will serve as a nice backup in case you screw things up attempting these changes. Otherwise, proceed with caution.

Update FSTAB on the instance to not use the UUID

As we go through the steps, one problem that was encountered is that when you essentially create a new instance using the original AMI you started with, the UUID of the root volume will be identical. This can cause a variety of problems, but the main issue I was having was that it would use the wrong volume if both the new and old volumes are attached to any instance. By updating FSTAB to not use UUIDs (at least temporarily), we can get around this problem. Here you can see the UUID in /etc/fstab that we are going to want to change to refer to by device id instead:

FSTAB using UUIDs

You can use nano, vi, or your favorite editor to make changes to that file. From the console you can find the device ID For the root partition / by typing df:

# df

Filesystem     1K-blocks     Used Available Use% Mounted on
devtmpfs         1843228        0   1843228   0% /dev
tmpfs            1879956        0   1879956   0% /dev/shm
tmpfs            1879956    72984   1806972   4% /run
tmpfs            1879956        0   1879956   0% /sys/fs/cgroup
/dev/nvme0n1p1  62903276 19720568  43182708  32% /
/dev/nvme1n1    73364480 54393696  18970784  75% /home
tmpfs             375988        0    375988   0% /run/user/1000

So you can see from this list the one that is mounted on / is /dev/nvme0n1p1. We will want to update /etc/fstab to use that instead of the UUID. The result will look something like this:

FSTAB using device ID instead

Once those changes are saved we are then ready to create our new instance.

Create a new instance using the original AMI

Let's spin up a new instance using the original AMI (Amazon Machine Image) that you used for your other instance. If you are unsure what that was, all you need to do is go to your instances in EC2, and click on the Instance ID for the instance in question. That instance summary page will show the AMI name as well as the AMI ID which will link to the AMI that was used:

EC2 Instance Summary showing AMI name and ID

You can see in this case we are using Rocky Linux 8. Make sure you configure your storage using your new preferred size for the root volume. In my case, I am trying to change the root volume size from 60GB to 30GB. So for this new instance, I will configure the EBS storage volume to be 30GB gp3:

Configure new EBS GP3 volume to be 30GB

Once you get all of your settings like you want click on the button to Launch Instance. The entire purpose of this is to find a way to make it relatively easy to create a new volume at your preferred size that can boot up correctly. You are welcome to log in to your instance to make sure it works, I like to run yum update to bring everything up-to-date, but I don't think that matters. Now shut down the newly created instance:

# shutdown -h now

You can confirm once your instance has stopped on the instances page in EC2:

Confirm EC2 instance has stopped

Now we are ready to associate this new volume to the original instance where we want to reduce the root volume.

Associate new instance volume with the pre-existing instance

In EC2 go to the Elastic Block Store section and click on Volumes. Go through the different volumes and correctly identify which is associated with your instance. If you scroll to the right and look at the column labeled Attached Instances, it should provide you clues on which one you are after. Check the box next to the volume that was just created with the new instance, and then click Actions and go to Detach Volume:

Detach the volume from the new instance

Once that is detached then you will want to shut down our original instance, the one with the larger root volume. Once shutdown, navigate back to the Volumes section and find the volume you recently detached which will say Available:

Volume instance is available for attaching

Select that volume again, go to Actions and click on Attach Volume. Now we want to attach this recently created volume to the same instance with the larger root volume. You can just use all the defaults when doing this:

Attach volume to instance using defaults

Once that is attached, go back to the instances section and restart your instance that now has the original root volume, as well as our newly created reduced size volume that will eventually be the new root volume.

Log in to your instance and from the console type:

# blkid

What you will notice is that because we essentially cloned a new root volume from the original AMI, you will have duplicate UUIDs listed, and this was the reason we had updated /etc/fstab earlier so that our machine would boot correctly when both volumes are attached. You will also notice that the device IDs that they are referred to are actually different:

blkid shows duplicate UUIDs

With the device IDs being different, one being /dev/nvme0n1p1 and the other /dev/nvme2n1p1, we were able to rely on that instead of the duplicate UUIDs.

Just a note, at this point you can change the UUID on the newly attached volume with the following command:

xfs_admin -U generate /dev/nvme2n1p1

However, if you do that you will also need to update grub's configuration and rebuild it otherwise your machine will not boot. This is not a necessary step for this tutorial so I am not going to include that information. You can always change the UUID back to the original as well if you made this mistake:

xfs_admin -U a15f4a29-7fb8-4725-ba35-a9a555640aaf /dev/nvme2n1p1

Another note that you may find useful is that you cannot change the UUID of the actively-being-used root volume, the only way to change the UUID of that is to do something similar and attach that volume to another EC2 instance as the non-root volume so that you can mess with things. Just remember if you do that, and you are wanting to use it as the root volume, you will need to update grub accordingly.

In our case, we are going to proceed with the duplicate UUIDs since we are going to be removing the duplicate volume shortly. With our UUID already set to the original UUID, we also won't need to do anything with grub which simplifies this process.

Our last step is to simply bring all of the files from your old root volume to your new one using rsync.

Rsync files from the root volume to the new volume

To be able to rsync files from the old root volume to the newly created smaller EBS volume that will eventually replace this, we first need to mount the new volume. First, recall the device ID From above, if needed type blkid again to identify it.

# mkdir /mnt
# mount -t xfs -o nouuid /dev/nvme2n1p1 /mnt

With this mount command, we have to pass in -o nouuid because as you recall we have a duplicate UUID. Without using that argument so that we ignore UUIDs, it would result in an error like the following:

mount: /mnt: wrong fs type, bad option, bad superblock on /dev/nvme2n1p1, missing codepage or helper program, or other error.

So the solution is to just make sure that we temporarily ignore UUIDs by using the nouuid argument. Now we can access this new volume at /mnt which you can confirm using the ls -la command:

Listing contents of new mount point

Now we are ready to rsync. Type the following to copy everything from the live root partition to our new root volume:

# rsync -aAXv --exclude={"/home/","/home","/dev/","/proc/","/sys/","/tmp/","/run/","/mnt/","/media/*","/lost+found","/","/mnt"} / /mnt

You can update the exclude as needed, but just a word of caution that I had to find out the hard way that you need to ensure that no spaces exist in the exclude text between commas. This will copy all the files from / to /mnt excluding the ones you want and the special categories. This will take a while, so sit back and relax, or find something else to work on while this happens.

Once this is complete, we are ready to boot with the new smaller root volume.

Use the new smaller volume with your instance

First shutdown your instance again:

# shutdown -h now

Then as we have done before in this tutorial, go ahead and detach the new volume, as well as the existing old root volume from the instance:

Detach both root volumes from the instance

Confirm that you really do want to detach these:

Confirm you want to detach volumes from instance

Now that both are detached, we are just going to attach the new smaller volume as we are ready to boot with it. The most critical step when attaching the volume is that you will need to ensure that the device name is as recommended below the text box for root volumes which is: /dev/sda1:

Set device name for the root volume when attaching

Now the only thing we have left to do is to go back to instances and start your instance back up! Once I log in I can confirm with the df -h command that the total disk space for the root partition is 30GB instead of 60GB:

Confirm root volume size is correct

Final Thoughts

The solution I provided above tries to avoid all of the pitfalls and technicalities I found in other solutions that just made things complicated, risky, or scary to perform. I hope many of you will find this useful.

The solution above will also work for non-root volumes, for example, this might be a common scenario for reducing the size of the /home volume which is a much more simple process. In this case, you would simply:

  1. Create a new volume at your desired size
  2. Attach the new volume to the instance
  3. Mount and rsync the files over

You can skip the process of creating a new instance using the original AMI, and you can skip the part of using the device id instead of the UUID in /etc/fstab. However, once you have everything transferred over, you will want to make sure you identify the new volume's UUID using blkid, and then update your FSTAB accordingly so that it's mounted when you boot up. You could then shutdown the instance, detach the larger volume you no longer want, and then restart the instance and confirm everything is correct.

This page was published on It was last revised on

Contributing Authors

0

2 Comments

  • Votes
  • Oldest
  • Latest
Commented

Thank you for this! This was the only guide that worked on my Ubuntu 20.04 instance.

add a comment
0
Commented

Hi!

This was immensely helpful.

Could you discuss how you constructed your rsync exclude list:

{"/home/","/home","/dev/","/proc/","/sys/","/tmp/","/run/","/mnt/","/media/*","/lost+found","/","/mnt"}

I understand mnt, but don't know enough about system internals to know if you excluded home or the other directories because you had them mounted on other boxes, or if you excluded them for specific reasons that would cause the new box to fail if they were copied.

  1. If it was a drive with two previous mount points (/backups and /data) and I want everything copied but them, should my exclude list add: "/backups","/data" to a base list of {"/","/mnt","/mnt/"}
  2. Why did you duplicate "/home/" and "/home" and "/mnt/" and "/mnt"? I'm guessing because home and mnt were mount points, but perhaps it has something to do with unix internals? Would I need to also include "/backups/" and "/data/" in the list above?
  3. Do I need to exclude the other elements on your list:
    "/dev/","/proc/","/sys/","/tmp/","/run/","/mnt/","/media/*","/lost+found"
    

    because they are system directories recreated on startup? If so, how did you decide on this list (which may be Linux platform-specific)?

I think explaining this would help guide users (and me) understand what is going on.

Thank you!

  • 1
    Great question! In the above tutorial if you read over it again, you can see when I was modifying fstab that there were three different Elastic Block Store (EBS) volumes that I had mounted: one for / , another for /home , and the last for SWAP. So for my case I excluded the /home directories because I was not doing anything with this volume even though it was mounted on the same box. I was only trying to reduce the volume size for the root / volume which is separate from the one with /home on it, so there was absolutely no reason for me to rsync that data too. For the other paths "/dev/","/proc/","/sys/" and some of the others like "/sys/","/tmp/","/run/" , these are all dynamically generated directories representing device nodes or special files. If I recall it may have been a bit of trial and error on my end figuring out exactly what I could skip. Regarding "/backups","/data" , yes you could add anything in addition you did not want to copy. The reason "/home/" and "/home" and other similar dupliates were on the list is because one represents just the directory, and the other represents everything under the directory. Notice one of each of the variants has a final backslash on them. — Brian Wozeniak
  • 2
    Thank you! I successfully used this method for an ext4 root volume and it worked (going from a ludicrously overprovisioned 500GB that had originally included our data and backups, to a svelt 14GB). One thing is that my ext4 root volume used labels rather than UUIDs. Because of that when I mounted it I wasn't sure whether or not to use the nouuid flag. I found you do need to use it and just remove the -t xfs flag. So my mount statement became: "mount -o nouuid /dev/nvme2n1p1 /mnt" and the rsync statement excluding /data and /backups became: rsync -aAXv --exclude={"/","/mnt/","/mnt","/data/","/data","/backups/","/backups","/dev/","/proc/","/sys/","/tmp/","/run/","/media/*","/lost+found"} / /mnt — Lyle_B
add a comment
1