Honey, I Shrunk the Snap!
by Igor Ljubuncic on 4 March 2021
The year is 1989. I bought a computer game called F-16: Combat Pilot, a flight simulator featuring free-flight, five types of single-player missions, a full campaign mode, serial-port multiplayer, and then some. Gloriously wrapped in four colors and magnetized on two single-density 5.25-inch floppy disks. Total size: 680 KB.
Nowadays, it is not uncommon for individual applications to weigh dozens if not hundreds of megabytes. But it doesn’t have to be that way. In Linux, you can save some space by using libraries that are shared across multiple applications (hence their name, shared libraries). When it comes to self-contained application formats like snaps, the tables are turned once again, as snaps bundle all the necessary dependencies inside, and thus take more disk space. If you want to make your snapped applications as small and lean as possible, we have a few neat suggestions.
Content snaps & extensions
Snaps can bundle all the necessary dependencies for an application to run – but they don’t need to. In fact, if you have several applications that use the same libraries, you can create a content snap, and then make it declare it for use in the snapcraft.yaml file using the content interface. This way, you can minimise the usage of code across multiple applications while still using the snap containment mechanism.
Better yet, you can use extensions, a framework designed to make snap usage more consistent, faster, and easier. We talked about extensions last year, with the KDE extension as an example. Similarly, there are several other supported extensions, including GNOME, ROS and Flutter.
In addition to having snaps behave in a more predictable way, extensions help you gain – or rather lose – size!
For instance, the KDE’s KCalc snap, which typically weighs around 100 MB as a standalone application, comes in at a very small, neat 972 KB – a 99% reduction from the original target and a number worth the 1980s gaming scene. Of course, the necessary libraries still need to exist somewhere – and they are contained in the KDE frameworks snap, which is used for all KDE applications.
The other important benefit of the extensions is that you also, most likely, gain in application startup performance, as we have outlined in our Chromium troubleshooting article from late last year. This way, you both save space and time.
Stage and prime
As we mentioned earlier, snaps bundle all the necessary dependencies required for the application to run. Sometimes, though, depending on how you build your snap, you may add files and folders that are not really needed. For example, man pages, localisation or perhaps specific libraries and binaries. Snapcraft allows you to build snaps from multiple types of sources, including git repositories, archives (like zip files), local folders with different assets, or by extracting data from DEB packages. In some cases, the files included in the sources may not be relevant for snaps, and you should remove them to save space.
There are two instances in the snap build process where you can make the necessary adjustments: during stage and prime steps:
- Stage is when components (parts) are copied into a staging area. Your snap could contain multiple parts, and each of these parts may have its own dependencies and assets. It is also possible that more than one part uses the same components (say a shared library), but you only really need to provide it once for your application to run. Here, you can make sure the list of items to be included (staged) contains no duplicates, multiple versions of the same library, or unnecessary runtime binaries.
- Prime is when components are copied into the priming area – very similar to what we saw in the staging area, minus the files needed for the build process itself. Here, you can do additional filtering and remove any objects that are not needed to run the application.
For instance, the code below will remove the listed directories and their contents from a built snap. This makes sense, because at runtime, you do not need header files, pkgconfig or docs.
The final artifact of the snap build process is a compressed squashFS file, with the .snap suffix. Originally, snaps were compressed using the xz algorithm, for highest compatibility with the widest range of devices. More recently, in order to help speed us snap launch times, we also introduced the use of the lzo algorithm, which results in 2-3x application startup times improvements. The main reason for this is the lesser compression used in lzo compared to xz, meaning the system needs fewer CPU cycles, and thus less time, to uncompress the snap on the system. However, it also introduces size inflation.
If you find the size to be of greater importance to you and your users, you can choose the xz algorithm for your snaps. It is the default value, so you can simply not declare any additional information. Alternatively, you can specify the compression in the snapcraft.yaml file, .e.g.:
Disk utilization matters less now than it did a decade or two ago, but you can still try to make your applications small and tidy. This also helps reduce bandwidth usage, improves portability, and if you’re using system backups, reduces the time needed to copy all the relevant data.
With snaps, there are many ways you can trim down on the digital excess, including the use of extensions, sparing use of necessary runtime dependencies, and pruning the extras from the prime directory. Not only will your snaps be smaller in size, you will also ensure higher consistency, better system integration and improve the application startup time. All these are important, highly noticeable elements of the user experience. If you have any other suggestions or ideas on how to conserve space or optimize snap creation, please join our forum and share your thoughts.