
Packaging Apps To Run on Any Linux Device
It's a Snap!
Those with a smattering of gray hair might be forgiven for mentioning that containers have been around for a relatively long time and that their evolution is not quite as magical as it may seem at first. Docker [1], however, is an undeniably fantastic advancement in the way infrastructure is created and operated. From a DevOps perspective, software deployments are no doubt possible considerably more often than before. It's unlikely in the long-term however that Docker will be the only popular container runtime daemon as rkt [2] and the Open Container Initiative [3] take off.
Since 2014, a major player in the Linux market has been touting a different type of container that is a real eye-opener. I'll take a look at Canonical's Snapcraft [4] package manager (also called the Snappy package manager), which is a concerted effort to mature and nurture a different way of placing applications in containers.
What, Where, Why
Ubuntu bravely states that snaps can "package any app for every Linux desktop, server, cloud or device, and deliver updates directly." As the product has matured, it's been used in Internet of Things (IoT) technologies and gained some momentum. The premise is that mobile devices, refrigerators, and parking meters should all be able to use portable containers packaged in a way that makes them device agnostic. In other words, you get to run lightweight applications without the fear that missing dependencies will break them. Intrigued? I think you should be.
If you're still not convinced, you should consider this note from the snaps GitHub [5] page before you get started:
Snaps are faster to install, easier to create, safer to run, and they update automatically and transactionally so your app is always fresh and never broken. You can bring your own build infrastructure or use ours.
When you install a snap for the first time, another small snap is also pulled down (at the time of writing, it's around 85MB), known as the core
or ubuntu-core
snap. It offers other snaps a read-only filesystem that can also rely on basic libraries, an init package (systemd), and networking. This is called an OS snap, because it is a minimal operating system of sorts. The clever part is that this OS snap allows you to run your snaps on any Linux distribution without fear of compatibility issues. Note that these OS snaps won't include the Apt package manager. I'll explain how to develop within an environment like this later.
The high-level view of a snap is that they're actually a SquashFS filesystem filled up with your application. To configure a snap, you mainly focus on a single pre-build config file called snapcraft.yaml
, which Ubuntu has described as a fancy ZIP file brimming with all the dependencies you'll ever need for your app. You'd be correct in noticing that YAML-formatted files are taking over the planet.
What are the fundamental differences between a Docker image and a snap, for example? A snap's raison d'être is usually to have a read-only view of the operating system for security reasons and piggyback its networking onto the host's network stack, whereas Docker containers instead focus on being more autonomous (e.g., with their own internal IP address).
Really, snaps are just a different type of container for your app, with the emphasis on being completely portable. The snap will generally be able to access other snaps because they're read-only, and it will have its own predefined, independent writeable filesystem. Systemd also comes into play, and your snap's services are handled by the init daemon in the same way as host services (i.e., stops and starts are handled for you).
One key thing to bear in mind is that, because the filesystems for these autonomous containers are locked away from the host's filesystem, it's important to use relative paths in your snap's configuration and not absolute paths. Otherwise, everything will break!
Strictly Speaking
From a security perspective, the read-only mode to keep successful attacks from persisting after a container stop/restart is very welcome. When running your snaps, you get to choose between three types of confinement: strict, devmode, and classic. The first, strict, is the default; it locks things down and lets you insist that your snap is restricted in only accessing its own install space. Figure 1 shows how this is controlled.
![Controlling filesystem access of default strict running mode (source: snapcraft.io [6]). Controlling filesystem access of default strict running mode (source: snapcraft.io [6]).](images/F01.png)
To dev or Not to dev
Developer mode (devmode) offers a much looser set of controls, in the sense that, rather than actions being blocked, warnings are sent to Syslog (usually into one of the /var/log/messages
or /var/log/syslog
files, depending on the distribution). Some detail in the debugging documentation [7] explains that you should always add the --devmode
option explicitly when you install a snap for sandbox work – even if you've loosened your security confinement settings within your YAML configuration file (snapcraft.yaml
).
It's a Classic
To run a snap in a no-holds-barred, throwing-caution-to-the-wind mode, you select --classic
, wherein the snap treats the host resources as its own. In the Snap Store, where a full catalog of snaps can be downloaded, any snaps set to run in classic mode are scrutinized by a human for safety reasons. I'll look in-depth about how you can develop your apps in classic mode in a moment.
Automatic Updates
Automatic updates are a natty addition. In the background, the current version of your snaps are checked against the latest versions and then updated if required on a daily basis. You can also use the refresh
option to make sure you have the latest versions.
Innards
The Snapcraft docs [8] explain some of the intricacies of which files do what inside a typical snap (not at the pre-build stage). Figure 2 shows the key files involved. It's important not to confuse the snap.yaml
and snapcraft.yaml
files. The latter is used before the snap is compiled (i.e., the pre-build stage I mentioned before). The main file I'll look at is the snapcraft.yaml
file. To whet your appetite, Listing 1 is a snippet from such a file, with some pertinent comments.
Listing 1: A Sample snapcraft.yaml File
--- name: Echo a name version: "1.0" # I'm a string not a number, so put me in quotes apps: echo-chrisbinnie: command: bin/echo # Note I drop the prepended slash to make it work inside a snap
![The key files inside a snap (source: The snap format [9]). The key files inside a snap (source: The snap format [9]).](images/F02.png)
In this basic example, you can see that this snippet is just "echoing" a string. Note that I'm intentionally using the relative path and not the usual absolute path, /usr/bin/host
.
Getting Your Hands Dirty
Now that I've touched on the basics, I'll create a snap that does something useful. All the major distributions support snapd
, from Arch Linux, Fedora, Gentoo, and openSUSE to OpenWrt and OpenEmbedded/Yocto, not to mention the obvious Debian and Ubuntu distributions.
I'll focus on my Linux Mint Debian laptop for convenience. To add the snapd
package, I use the oh-so-arduous command (which might not even be necessary on some of the newer Ubuntu versions; I've read it's built in from 16.04 onward):
$ sudo apt install snapd
Snaps are usually pulled from the Snap Store [10], which is comparable to the Docker Hub. To get going, it's a case of searching for your snap. Again, this process isn't too dissimilar to using the Docker Hub. You'll find a lot of pre-built snaps to work with.
Shopping
If you don't have an Ubuntu One account, simply sign up. You'll also be shown the way to add your own snaps to the Snap Store once you've logged in. The marketing blurb includes the promise of getting your snap published within minutes and being able to reach millions of devices through the Snap Store.
Next, you will want to log in to the Snap Store:
$ snap login chris@binnie.tld
You should see a Login successful message.
Finding Nemo
Figure 3 shows a search using the keyword host
. This is a command that you'll be using frequently with snap if you're pulling in other people's snaps. Instead of search, as in Docker, the Snap Store uses find to look for useful snaps.

To install one of the newly found packages, just use the install
command (Figure 4). Opting sensibly for the second package from the top, because it's from Canonical, I use the command:

$ snap install wifi-ap
In the console, you can see the snap output as it installs successfully.
Shopping Lists
Another command you'll need frequently is the list
command, which shows your installed snaps. Figure 5 shows two locally installed snaps: core
and wifi-ap
, both created by Canonical.

list
command displays local snap information.A quick glance at what the docs have to say about a Hello World! snap can be found on the Usage page [11]. I remember when I first saw this, I was surprised to see the output without any prepended command on the host's command prompt (i.e., no snap
command). I just typed hello
after installing the snap:
$ hello Hello, world!
The output denotes success.
Remember the wifi-ap
snap? This command has a lot of juicy information, which you can see by entering the info
command (Listing 2). The description section should be self-explanatory; just below that, a URL points you to the snap's source code [12]. Also note the options listed under the commands section, which lists the possible parameters you can send to the snap.
Listing 2: Getting wifi-ap Snap Information
root@chris:~# snap info wifi-ap name: wifi-ap summary: "WiFi Access Point based on hostapd" publisher: canonical contact: snappy-canonical-storeaccount@canonical.com description: | This snap is implementing a WiFi access point based on hostapd and allows easily to share a internet connection or just create a network others can easily connect to. Please find the source of this snap at: https://code.launchpad.net/~snappy-hwe-team/snappy-hwe-snaps/+git/wifi-ap commands: - wifi-ap.config - wifi-ap.setup-wizard - wifi-ap.status tracking: stable installed: 15 (146) 31MB - refreshed: 2017-05-05 16:20:19 +0000 UTC channels: stable: 15 (146) 31MB - candidate: 15 (146) 31MB - beta: 16 (186) 31MB - edge: 17-dev (194) 31MB -
If you follow the source link to the Ubuntu Launchpad site, navigate to the Get this repository section, and then run the git clone
command with the -b
(branch) switch (Listing 3), you can download the latest source. If you then enter the wifi-ap
directory, you can see, among all the small files, the all-important snapcraft.yaml
file.
Listing 3: Getting the Latest wifi-ap Source Code
root@chris:~# git clone -b master https://git.launchpad.net/~snappy-hwe-team/snappy-hwe-snaps/+git/wifi-ap Cloning into 'wifi-ap'... remote: Counting objects: 2220, done. remote: Compressing objects: 100% (1903/1903), done. remote: Total 2220 (delta 882), reused 955 (delta 182) Receiving objects: 100% (2220/2220), 2.71 MiB | 0 bytes/s, done. Resolving deltas: 100% (882/882), done. Checking connectivity... done.
Snap, Crackle, and Pop
Now I'll look at how you can create your own very basic snap. In this example, you just want to perform a super-simple echo
command of whatever input (STDIN) is typed. To develop your own snaps, you need to install the snapcraft package:
$ apt install snapcraft
After a little more than 8MB of files are installed, you can start creating a snap with an init
command (Figure 6), which pulls in a template.
Because I've already run hello
and wifi-ap
, those snaps are in the snap/
directory, along with the snapcraft.yaml
file:
root@chris:~# ls snap/ core hello snapcraft.yaml wifi-ap
Listing 4 shows the intuitive and easy-to-follow template file.
Listing 4: A snapcraft.yaml Template File
name: my-snap-name # you probably want to 'snapcraft register <name>' version: '0.1' # just for humans, typically '1.2+git' or '1.3.2' summary: Single-line elevator pitch for your amazing snap # 79 char long summary description: | This is my-snap's description. You have a paragraph or two to tell the most important story about your snap. Keep it under 100 words though, we live in tweetspace and your description wants to look good in the snap store. grade: devel # must be 'stable' to release into candidate/stable channels confinement: devmode # use 'strict' once you have the right plugs and slots parts: my-part: # See 'snapcraft plugins' plugin: nil
Classic Chroots
With Ubuntu 16.04 onward, many of the snap tools required for building a snap appear to be readily available.
To avoid read-only filesystem issues (i.e., no useful package manager like Apt), you can create a chroot environment in which you develop your application. A chroot ignores the file path and some of the host system innards. Within the chrooted environment, you share the home/
directory between the container (your snap) and the ubuntu-core
snap (your snap's OS). The next command lets you run a "classic" Ubuntu environment on a Snappy system:
$ snap install classic --edge --devmode classic (edge) 16.04 from 'canonical' installed
When you run classic
, it changes the prompt, after loads of output, by prepending (classic), which is also a reminder to always run this from your home directory so snapd
knows what's going on.
$ classic (classic)root@chris:~#
Unlike my example, you should probably avoid using root and, if needed, sudo
from your user or use it directly.
You need to throw in some compilers and other build tools now, which, if your prompt has the classic prefix, gets chucked into your chroot and not directly onto your system:
(classic)root@chris:~# apt install build-essential
If you did not install the snapcraft package before this point, you should add that package to this command, as well. After hitting the Enter key, you're faced with the usual (potentially insecure, if you leave it on a system unnecessarily) more than 200MB of compiler tools, such as the omnipotent gcc package.
Make It Snappy
Unlike the init
command example in Figure 6, in this case, I'm running it inside my chroot. For this example, I create and change to a directory called echo-chrisbinnie
before running the init
command. Pay close attention: If you're not in a directory off your home directory, your chroot might not work as expected and could potentially reference other system paths within the snap by accident.

In a slightly alarming oh-no-it's-all-badly-broken sort of way, you might see a screen full of errors. Fret not, though, because on Ubuntu 16.04, I entered two suggested export
commands,
$ export LC_ALL=C.UTF-8; export LANG=C.UTF-8
and then ran the snapcraft init
command again with complete success. Thankfully, it's happy days again when you see the friendly Edit the file to your liking greeting that you saw in Figure 6. As you've probably guessed, the next thing to do is edit the template file under the snap/
directory.
When I first looked at snaps, I have to admit that I needed to tinker with paths, and source file downloads, but ultimately, unless you're building a massive file from source, it's a relatively simple and rewarding experience to build your first snap. Once your YAML is correct, you witness a welcome screen of progress as your snap is being packaged (Figure 7). To compile your snapcraft.yaml
file, just make sure you're in the snap/
directory within your chroot, and run the
(classic)root@chris:~/echo-chrisbinnie/snap# snapcraft
command a few times, adjusting your configuration according to any errors. Also, pay attention to the configflags
compile flags (Listing 5, last line). Would you believe even a simple echo
command needs tweaking with compile flags?!
Listing 5: Working Config for Echo Snap
name: echo-chrisbinnie version: '0.1' summary: Echo a name description: | Enter a name and I will echo it. grade: devel confinement: devmode apps: echo-chrisbinnie: command: bin/echo bash: command: bash parts: echo-chrisbinnie: source: https://launchpad.net/ubuntu/+archive/primary/+files/coreutils_8.25.orig.tar.xz plugin: autotools configflags: ["FORCE_UNSAFE_CONFIGURE=1"]
If you get stuck, you'll find more docs [13] at the Ubuntu developer site that are much easier to use if you spin up a fully supported (ephemeral) Ubuntu 16.04 cloud instance/droplet/VM somewhere. Note that I simply looked up Ubuntu's 16.04 source files online for the coreutils URL (which is the package that provides the echo
binary).
Once compilation and packaging was complete, I found an echo-chrisbinnie_0.1_amd64.snap
file inside my snap/
directory. If you're happy that your version worked like mine (Figure 7), you can change confinement
from devmode
to strict
and grade
from devel
to stable
for publishing.

snapcraft
command in a chrooted environment; this output indicates everything is working as hoped (probably!).Now, you need to run the packaging command one more time:
$ snapcraft
Assuming your last line says Snapped with the snap name (and you're not uploading the snap to the Snap Store but installing it locally), you'll face the moment of truth when you try to execute your snap.
Oh No, It's Snapped!
Because your hand-crafted snap hasn't been signed by Canonical's Snap Store at this stage, you'll have to add --dangerous
to the end of the install
command. (By this stage, to publish your snap successfully, you have to have edited the config file with the changes to confinement
and grade
, as mentioned in the previous section). Even though confinement
is strict
, your snap is still apparently dangerous. Don't forget to run the snapcraft
command whenever you make a change to your config file.
A simple snap installation looks like this example:
(classic)root@chris: ~/echo-chrisbinnie/snap# snap install echo-chrisbinnie_0.1_amd64.snap --dangerous echo-chrisbinnie 0.1 installed
It's a Snap
Once you're happy that your snap is working, you should alter the name in your config file so that it will be appropriate and unique in the Snap Store. The next step is to upload to the Snap Store. If you've used Docker, the purpose of the following commands are easy to guess:
$ snapcraft login $ snapcraft register echo-chrisbinnie $ snapcraft push echo-chrisbinnie_0.1_amd64.snap --release=candidate
You need an Ubuntu One login for the first step; the second step reserves a name(space) for your snap. The --release
option in the last line indicates that you are uploading to the candidate
channel.
You should receive email saying that a number of unassisted robots are currently pouring over your code to make sure it's not going to combust spontaneously if someone tries to use it. After it's given a titanium thumbs-up, it will be available in the Snap Store.
The docs remind you that you can't push snaps with confinement
set to devmode
to either the stable
or candidate
channels. Figure 8 shows a successful upload; a few moments later, an Upload Status: Approved email should hit your inbox.

Keep It Snappy
To install this snap from the Snap Store after it's been automagically cleared for usage, it's simply a matter of entering the command:
$ snap install echo-chrisbinnie --channel=candidate
Outside of my chroot, directly on my host prompt, I can now see how my command works:
$ echo-chrisbinnie Chris Loves Linux Chris Loves Linux
The End
According to the docs, "when you have a working snap, the next step is to use build.snapcraft.io
to link your code repository and automate amd64
and armhf
builds and releases." This apparently allows fully functional compatibility across many devices.
I hope to get some free time to experiment further with Ubuntu's snaps. Certainly, after a relatively cursory inspection, they hold lots of promise. Although I feel a little late to the party, I'm pleased with the nice, warm feeling knowing that the product has matured somewhat.
The security aspects in snaps make lots of sense for IoT devices and the like, considering that strict
confinement is enforced by default. Over time, I'm sure the process of creating snaps will become second nature.
Although the container landscape shifts with changes in power and popularity, you should not dismiss the snaps concept. Should you want to delve deeper into this area, you might also want to look at a very similar product called Flatpak [14]. I hope this article has provided enough food for thought that you will give at least one of these products a try.