A long time ago, a Linux distribution shipped an operating system along with all the software available for it. There was no concept of “third party” software because everything was a part of the distribution. Applications weren’t so much installed as they were enabled from a great big software repository that you got on one of the many floppy disks or, later, CDs you purchased or downloaded.
This evolved into something even more convenient as the internet became ubiquitous, and the concept of what is now the “app store” was born. Of course, Linux distributions tend to call this a software repository or just repo for short, with some variations for “branding”, such as Ubuntu Software Center or, with typical GNOME minimalism, simply Software.
This model worked well back when open source software was still a novelty and the number of open source applications was a number rather than a theoretical number. In today’s world of GitLab and GitHub and Bitbucket (and many many more), it’s hardly possible to count the number of open source projects, much less package them up in a repository. No Linux distribution today, even Debian and its formidable group of package maintainers, can claim or hope to have a package for every installable open source project.
Of course, a Linux package doesn’t have to be in a repository to be installable. Any programmer can package up their software and distribute it from their own website. However, because repositories are seen as an integral part of a distribution, there isn’t a universal packaging format, meaning that a programmer must decide whether to release a
.rpm, or an AUR build script, or a Nix or Guix package, or a Homebrew script, or just a mostly-generic
.tgz archive for
/opt. It’s overwhelming for a developer who lives and breathes Linux every day, much less for a developer just trying to make a best-effort attempt at supporting a free and open source target.
The Flatpak project provides a universal packaging format along with a decentralized means of distribution, plus portability, and sandboxing.
- Universal Install the Flatpak system, and you can run Flatpaks, regardless of your distribution. No daemon or systemd required. The same Flatpak runs on Fedora, Ubuntu, Mageia, Pop OS, Arch, Slackware, and more.
- Decentralized Developers can create and sign their own Flatpak packages and repositories. There’s no repository to petition in order to get a package included.
- Portability If you have a Flatpak on your system and want to hand it to a friend so they can run the same application, you can export the Flatpak to a USB thumbdrive.
- Sandboxed Flatpaks use a container-based model, allowing multiple versions of libraries and applications to exist on one system. Yes, you can easily install the latest version of an app to test out while maintaining the old version you rely on.
Building a Flatpak
To build a Flatpak, you must first install Flatpak (the subsystem that enables you to use Flatpak packages) and the Flatpak-builder application.
On Fedora, CentOS, RHEL, and similar:
$ sudo dnf install flatpak flatpak-builder
On Debian, Ubuntu, and similar:
$ sudo apt install flatpak flatpak-builder
You must also install the development tools required to build the application you are packaging. By nature of developing the application you’re now packaging, you may already have a development environment installed, so you might not notice that these components are required, but should you start building Flatpaks with Jenkins or from inside containers, then you must ensure that your build tools are a part of your toolchain.
For the first example build, this article assumes that your application uses GNU Autotools, but Flatpak itself supports other build systems, such as
ant, as well as custom commands (a
simple build system, in Flatpak terminology, but by no means does this imply that the build itself is actually simple).
Unlike the strict RPM build infrastructure, Flatpak doesn’t impose a project directory structure. I prefer to create project directories based on the dist packages of software, but there’s no technical reason you can’t instead integrate your Flatpak build process with your source directory. It is technically easier to build a Flatpak from your dist package, though, and it’s an easier demo too, so that’s the model this article uses. Set up a project directory for GNU Hello, serving as your first Flatpak:
$ mkdir hello_flatpak $ mkdir src
Download your distributable source. For this example, the source code is located at
$ cd hello_flatpak $ wget https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz
A Flatpak is defined by a manifest, which describes how to build and install the application it is delivering. A manifest is atomic and reproducible. A Flatpak exists in a “sandbox” container, though, so the manifest is based on a mostly empty environment with a root directory call
The first two attributes are the ID of the application you are packaging and the command provided by it. The application ID must be unique to the application you are packaging. The canonical way of formulating a unique ID is to use a triplet value consisting of the entity responsible for the code followed by the name of the application, such as
org.gnu.Hello. The command provided by the application is whatever you type into a terminal to run the application. This does not imply that the application is intended to be run from a terminal instead of a
.desktop file in the Activities or Applications menu.
In a file called
org.gnu.Hello.yaml, enter this text:
id: org.gnu.Hello command: hello
A manifest can be written in YAML or in JSON. This article uses YAML.
Next, you must define each “module” delivered by this Flatpak package. You can think of a module as a dependency or a component. For GNU Hello, there is only one module: GNU Hello. More complex applications may require a specific library or another application entirely.
modules: - name: hello buildsystem: autotools no-autogen: true sources: - type: archive path: src/hello-2.10.tar.gz
buildsystem value identifies how Flatpak must build the module. Each module can use its own build system, so one Flatpak can have several build systems defined.
no-autogen value tells Flatpak not to run the setup commands for
autotools, which aren’t necessary because the GNU Hello source code is the product of
make dist. If the code you’re building isn’t in a easily buildable form, then you may need to install
autoconf to prepare the source for
autotools. This option doesn’t apply at all to projects that don’t use
type value tells Flatpak that the source code is in an archive, which triggers the requisite unarchival tasks before building. The
path points to the source code. In this example, the source exists in the
src directory on your local build machine, but you could instead define the source as a remote location:
modules: - name: hello buildsystem: autotools no-autogen: true sources: - type: archive url: https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz
Finally, you must define the platform required for the application to run and build. The Flatpak maintainers supply runtimes and SDKs that include common libraries, including
kde. The basic requirement is the
freedesk runtime and SDK, although this may be superseded by GNOME or KDE, depending on what your code needs to run. For this GNU Hello example, only the basics are required.
runtime: org.freedesktop.Platform runtime-version: '18.08' sdk: org.freedesktop.Sdk
The entire GNU Hello flatpak manifest:
id: org.gnu.Hello runtime: org.freedesktop.Platform runtime-version: '18.08' sdk: org.freedesktop.Sdk command: hello modules: - name: hello buildsystem: autotools no-autogen: true sources: - type: archive path: src/hello-2.10.tar.gz
Building a Flatpak
Now that the package is defined, you can build it. The build process prompts Flatpak-builder to parse the manifest and to resolve each requirement: it ensures that the necessary Platform and SDK are available (if they aren’t, then you’ll have to install them with the
flatpak command), it unarchives the source code, and executes the
The command to start:
$ flatpak-builder build-dir org.gnu.Hello.yaml
build-dir is created if it does not already exist. The name
build-dir is arbitrary; you could call it
penguin, and you can have more than one build destination in the same project directory. However, the term
build-dir is a frequent value used in documentation, so using it as the literal value can be helpful.
Testing your application
You can test your application before or after it has been built by running the build command along with the
--run option, and ending the command with the command provided by the Flatpak:
$ flatpak-builder --run build-dir \ org.gnu.Hello.yaml hello Hello, world!
Packaging GUI apps with Flatpak
Packaging up a simple self-contained hello world application is trivial, and fortunately packaging up a GUI application isn’t much harder. The most difficult applications to package are those that don’t rely on common libraries and frameworks (in the context of packaging, “common” means anything not already packaged by someone else). The Flatpak community provides SDKs and SDK Extensions for many components you might otherwise have had to package yourself. For instance, when packaging the pure Java implementation of
pdftk, I use the OpenJDK SDK extension I found in the Flatpak Github repository:
runtime: org.freedesktop.Platform runtime-version: '18.08' sdk: org.freedesktop.Sdk sdk-extensions: - org.freedesktop.Sdk.Extension.openjdk11
The Flatpak community does a lot of work on the foundations required for applications to run upon in order to make the packaging process easy for developers. For instance, the Kblocks game from the KDE community requires the KDE platform to run, and that’s already available from Flatpak. The additional
libkdegames library is not included, but it’s as easy to add it to your list of
Here’s a manifest for the Kblocks game:
id: org.kde.kblocks command: kblocks modules: - buildsystem: cmake-ninja name: libkdegames sources: type: archive path: src/libkdegames-19.08.2.tar.xz - buildsystem: cmake-ninja name: kblocks sources: type: archive path: src/kblocks-19.08.2.tar.xz runtime: org.kde.Platform runtime-version: '5.13' sdk: org.kde.Sdk
As you can see, the manifest is still straight-forward and relatively intuitive. The build system is different, and the runtime and SDK point to KDE instead of the Freedesktop, but the structure and requirements are basically the same.
Because it’s a GUI application, however, there are some new options required. First, it needs an icon so that when it’s listed in the Activities or Application menu, it looks nice and recognizable. Kblocks includes an icon in its sources, but the names of files exported by a Flatpak must be prefixed using the application ID (such as
org.kde.Kblocks.desktop). The easiest way to do this is to rename the file directly in the application source, which Flatpak can do for you as long as you include this directive in your manifest:
Another unique trait of GUI applications is that they often require integration with common desktop services, like the graphics server (X11 or Wayland) itself, a sound server such as Pulse Audio, and the Inter-Process Communication (IPC) subsystem.
In the case of Kblocks, the requirements are:
finish-args: - --share=ipc - --socket=x11 - --socket=wayland - --socket=pulseaudio - --device=dri - --filesystem=xdg-config/kdeglobals:ro
Here’s the final, complete manifest, using URLs for the sources so you can try this on your own system easily:
command: kblocks finish-args: - --share=ipc - --socket=x11 - --socket=wayland - --socket=pulseaudio - --device=dri - --filesystem=xdg-config/kdeglobals:ro id: org.kde.kblocks modules: - buildsystem: cmake-ninja name: libkdegames sources: - sha256: 83456cec44502a1f79c0be00c983090e32fd8aea5fec1461fbfbd37b5f8866ac type: archive url: https://download.kde.org/stable/applications/19.08.2/src/libkdegames-19.08.2.tar.xz - buildsystem: cmake-ninja name: kblocks sources: - sha256: 8b52c949e2d446a4ccf81b09818fc90234f2f55d8722c385491ee67e1f2abf93 type: archive url: https://download.kde.org/stable/applications/19.08.2/src/kblocks-19.08.2.tar.xz rename-icon: kblocks runtime: org.kde.Platform runtime-version: '5.13' sdk: org.kde.Sdk
To build the application, you must have the KDE Platform and SDK Flatpaks (version 5.13 as of this writing) installed. Once the application has been built, you can run it using the
--run method, but to see the application icon, you must install it.
Distributing and installing a Flatpak you have built
Distributing flatpaks happen through repositories.
You can list your apps on Flathub.org, a community website meant as a technically decentralised (but central in spirit) location for Flatpaks. To submit your Flatpak, place your manifest into a Git repository and submit a pull request on Github.
Alternately, you can create your own repository using the
flatpak build-export command.
You can also just install locally:
$ flatpak-builder --force-clean --install build-dir org.kde.Kblocks.yaml
Once installed, open your Activities or Applications menu and search for Kblocks.
The Flatpak documentation site has a good walkthrough on building your first Flatpak. It’s worth reading even if you’ve followed along with this article. Besides that, the docs provide details on what Platforms and SDKs are available.
For those who enjoy learning from examples, there are manifests for every application available on Flathub.
The resources to build and use Flatpaks are plentiful, and Flatpak, along with containers and sandboxed apps, are arguably the future, so get familiar with them, start integrating them with your Jenkins pipelines, and enjoy easy and universal Linux app packaging.