Clean Up the Root Partition Using Btrfs & Snapper


Snapper is a nice snapshot tool for easy backup and rollback with btrfs. However, its default maneuver over snapshots can be a PITA as it frequently takes snapshots which by all means seem unnecessary.

For example, the default snapper config (named root) on OpenSUSE will take a snapshot whenever you make a change with zypper (the package management tool for OpenSUSE). That means if you install a new python package, BANG!, two snapshots are added. (one is a pre and one is a post, which are snapshots usually come in pairs.) This will eat up the space of your root partition extremely fast.

The solution is rather simple too, you can:

  1. Ask snapper to clean up the old snapshots more aggressively.

    So that they won't just sit the fat ass doing nothing but eat up your precious disk space. You will find the config file usually in /etc/snapper/configs, and can change the following parameters.

        # run daily number cleanup
        NUMBER_CLEANUP="yes"
    
        # limit for number cleanup
        NUMBER_MIN_AGE="1800"
        NUMBER_LIMIT="5"
        NUMBER_LIMIT_IMPORTANT="3"
    
        # create hourly snapshots
        TIMELINE_CREATE="no"
    
        # cleanup hourly snapshots after some time
        TIMELINE_CLEANUP="yes"
    
        # limits for timeline cleanup
        TIMELINE_MIN_AGE="1800"
        TIMELINE_LIMIT_HOURLY="10"
        TIMELINE_LIMIT_DAILY="10"
        TIMELINE_LIMIT_WEEKLY="0"
        TIMELINE_LIMIT_MONTHLY="10"
        TIMELINE_LIMIT_YEARLY="1"
    
        # cleanup empty pre-post-pairs
        EMPTY_PRE_POST_CLEANUP="yes"
    
        # limits for empty pre-post-pair cleanup
        EMPTY_PRE_POST_MIN_AGE="1800"
        
    There are two ways of cleanup: by number (NUMBER_CLEANUP) and by time (TIMELINE_CLEANUP). The former deletes snapshots depending on how many snapshots are there, and the later makes decision about how old the snapshots are. The parameters are pretty much self-explained. I myself find TIMELINE_LIMIT_**** a little confused. Take TIMELINE_LIMIT_WEEKLY="2" as an example, it means a snapshot is taken each week (at the end of that week) and will be kept for 2 weeks before being removed. Similar meaning goes for hour, month and year.

  2. Create snapshots less frequently.

    If you are on OpenSUSE, the snapper behavior is also connected to zypper. The config file is /etc/snapper/zypp-plugin.conf, which looks like something below.

        <?xml version="1.0" encoding="utf-8"?>
        <snapper-zypp-plugin-conf>
          <!-- List of solvables. If the zypp commit transaction includes any solvable
               listed below, snapper will create pre and post snapshots. The match
               attribute defines whether the pattern is a Unix shell-style wildcard
               ("w") or a Python regular expression ("re"). If any of the matching
               solvables is marked as important, the snapshots are also marked as
               important. -->
          <solvables>
            <solvable match="w" important="true">kernel-*</solvable>
            <solvable match="w" important="true">dracut</solvable>
            <solvable match="w" important="true">glibc</solvable>
            <solvable match="w" important="true">systemd*</solvable>
            <solvable match="w" important="true">udev</solvable>
            <!--<solvable match="w">*</solvable>-->
          </solvables>
        
    Usually, comment out the line <solvable match="w">*</solvable> like I do here should be enough to avoid those annoying trivial snapshots.

  3. Remove existing snapshots.

    With snapper list you can see what snapshots are there at the moment.

        $ snapper list
        Type   | #  | Pre # | Date                            | User | Cleanup | Description           | Userdata     
        -------+----+-------+---------------------------------+------+---------+-----------------------+--------------
        single | 0  |       |                                 | root |         | current               |              
        single | 1  |       | Sun 20 Mar 2016 07:56:04 AM CST | root |         | first root filesystem |              
        pre    | 6  |       | Thu 21 Dec 2017 11:50:47 AM CST | root | number  | yast firewall         |              
        post   | 7  | 6     | Thu 21 Dec 2017 11:51:11 AM CST | root | number  |                       |              
        pre    | 8  |       | Thu 21 Dec 2017 11:52:04 AM CST | root | number  | yast services-manager |              
        post   | 9  | 8     | Thu 21 Dec 2017 11:52:48 AM CST | root | number  |                       |              
        pre    | 10 |       | Thu 21 Dec 2017 11:57:48 AM CST | root | number  | yast firewall         |              
        post   | 11 | 10    | Thu 21 Dec 2017 12:04:20 PM CST | root | number  |                       |              
        pre    | 12 |       | Mon 25 Dec 2017 01:50:57 PM CST | root | number  | zypp(zypper)          | important=ye
        
    And you can delete the unwanted ones with snapper delete SNAPSHOT_NUMBER. Generally, the older a snapshot, the larger space it takes.

    In some special cases, snapshots will escape from the list for some weird God-knows-what reasons, and you won't see them in the output of snapper list. If you manually check /.snapshots/, you will find numbered folders storing the corresponding orphaned snapshots. However, if you try to rm -rf them, the system will prompt that you have no permission, even if you are in root. Now there comes the one million dollar trick:

  4. Remove orphaned legacy snapshots.

    You have to first delete the subvolumes that hold these snapshots, then remove them with rm. Let's take snapshot 102 as an example:

        $ btrfs subvolume delete /.snapshots/102/snapshot
        Delete subvolume (no-commit): '/.snapshots/102/snapshot'
        $ rm -rf /.snapshots/102/ 
        
    These orphaned snapshots can be real problematic over time (cost me 15+ GB). So if you find that the snapper list is almost empty but the root partition is still crowded as hell, maybe it's time to take a look at /.snapshots/.

    Last but not least, always be very careful because those snapshots listed by snapper list are also stored here, always check if the snapshot is an orphan before use this method to remove it. If not, use snapper delete as stated in 3.

More details can be found in this nice snapper manpage.

By