Flatpak – a history

I’ve been working on Flatpak for almost 4 years now, and 1.0 is getting closer. I think it might be interesting at this point to take a retrospective look at the history of Flatpak.

Early history

Ancient Egyptian Flatpak

The earliest history goes back to the summer of 2007. I had played a bit with a application image system called Klik, which had some interesting ideas. However, I was not really satisfied with some technical details. One day at the beach I got an interesting ideas for a hack that could improve this.

Fast forward until August 2007 when I released Glick in the wild, based on these ideas. The name is sort of a pun on the old KDE/Gnome first-letter naming scheme, although neither Klik or Glick are really desktop-specific.

Glick was a a single-file-image system. It predates usable kernel container APIs, so it uses fuse and some weird hacks. It doesn’t integrate with the desktop in any way, and applications have to decide what to bundle, falling back to system-libraries for the non-bundled things. This means its not terribly robust., but it is completely stand-alone and need nothing installed on the host system.

Around 2011 the initial support for kernel namespaces had landed and started being useful. Using these I could avoid some of the hacks that my earlier experiment used. So, I got interested in bundling again and released Glick 2 based on this.

Glick 2 requires some software to be installed on the host, which allows it to integrate better with the system. For example, you can “install” bundles by putting the file in a known location, and doing this allows some level of desktop integration. Glick 2 also uses SHA1 checksums to try to automatically de-duplicate files shared between applicatins. Here we can see an early version of the ideas that make up OSTree.

Bundling using namespaces was lot more robust than the previous hacks, but it still relied on the system for the core libraries that the application doesn’t bundle. So an app would sometimes work on one distro, but not another.

Around this time I posted a blog  about how I thought application bundling combined with read-only OS images can make a really good model for an OS. This idea is very similar to what Project Atomic / SilverBlue  are doing now.

Containers, Portals and Runtimes

A few years later, around 2013 the kernel support for containers was starting to shape up, and Docker hit the market. I did a lot of work on the early docker, like porting it away from aufs in order to run on RHEL.

Around this time I also attended the Gnome Developer Experience hackfest  in Brussels where one of the topics was Application deployment and sandboxing. From the discussions there (and my previous experiences) a lot of the core ideas of Flatpak, like runtimes, sandboxing and portals originated.

In 2014 the first version (then called xdg-app) was released. The current Flatpak is a lot more polished, but the initial version of xdg-app is still very much recognizable today.

xdg-app used OSTree to download, store and de-duplicate applications. It uses kernel namespaces (via a helper called xdg-app-helper) to do unprivileged containers. It has a split between applications and runtimes which allow applications to be portable between distros in a very robust fashion, while still limiting the duplication between applications and allowing security updates. There is also integration with the desktop (icons, desktop files, mimetypes, etc), and some very early portal code can be seen.

The great renaming

Modern Flatpak

The name xdg-app was just something I picked for the first commit without much consideration, and it was not very good. However, names are hard, and we spent a lot of time trying to come up with another, eventually settling on “Flatpak” (with the above logo). The 0.6.0 release in may 2016 was the first with the new name.

The 0.6 release was also the first that split out the unprivileged container launcher (xdg-app-helper) into its own project, now known as BubbleWrap , hosted by Project Atomic.

Soon thereafter we had the first release of xdg-desktop-portal which is the host-side implementation of the portal idea, allowing sandboxed applications to safely break out of the sandbox in a controlled fashion.

Version 0.8.0, released december 2016 was the first long-term stable release, which was included in Debian Stretch and RHEL 7. Since then we have had another stable release series called 0.10.x.

We want apps!

Flatpak was always a decentralized system, in that anyone can host their own applications and be on equal terms with everyone else. However, while this is an important feature, it leads to a poor initial experience, both for users (hard to find apps) or developers (need to maintain their own repository).

To solve this we started the Flathub project, which is a single repository where you can find most apps. In the last year it has gone from a minimal viable product building its first app to something with more than 300 apps and a diverse group of developers.

Onwards and upwards!

Future Flatpak

No software is ever finished, or bug-free, but we have had a list of core things that we wanted to have before calling Flatpak 1.0, and that list is now empty. So, I’m planning to release a release-candidate (called 0.99.1) later this week.

Then 1.0 will be released later this summer.

Introducing the 1.8 freedesktop runtime in the gnome nightly builds

All the current Flatpak runtimes in wide use are based on the 1.6 Freedesktop runtime. This is a two-layered beast where the lower layer is built using Yocto and the upper layer is built using flatpak-builder.

Yocto let us get a basic system bootstrapped, but it is not really a great match for the needs of a Flatpak runtime. We’ve long wanted a base that targeted sandboxed builds which is closer to upstream and without the embedded cross-compiled legacy of Yocto. This is part of the reason why Tristan started working on BuildStream, and why the 1.8 Freedesktop runtime was created from scratch, using it.

After a herculean effort from the people at Codethink (who sponsor this effort) we now have a working Yocto-free version of the Platform and Sdk. You can download the unstable version from here and start playing with it. It is not yet frozen or API/ABI stable, but its getting there.

The next step in this effort is to test it as widely as possible to catch any issues before the release is frozen. In order to do this I rebased the Gnome nightly runtime builds on top of the new Freedesktop version this week. This is a good match for a test release, because it has no real ABI requirements (being rebuilt with the apps daily), yet gets a fair amount of testing.

WARNING: During the initial phase it is likely that there will be problems. Please test your apps extra carefully and report all the issues you find.

In the future, the goal is to also convert the Gnome runtimes to BuildStream. Work on this has started, but for now we want to focus on getting the base runtime stable.

YAMLing the flathub

The most common way to build flatpak is using a tool called flatpak-builder. This is a tool that takes a higher level description of the sources that go into an application and generate the build commands to build it. This description is called a manifest, and is traditionally a JSON file.

JSON is very common in the web world, and it is a well known format that have many implementations. However, it is not really great for humans to write.

For example, JSON has no support for comments, which you typically want in something as complicated as a build script. There are ways to work around this,  but it is not pretty.

The syntax, while easy for a machine to parse, is very verbose and noisy to read and picky to write. For example, all lists have to be comma separated, but you can’t have a trailing comma, which you often run into when deleting or moving items around. Also, both keys and values always have to be quoted, which feels pretty unnecessary for the simple one-word keys and values that are common case in manifest files.

This is what a JSON manifest look like:

{
  "id": "org.gnome.frogr",
  "runtime": "org.gnome.Platform",
  "sdk": "org.gnome.Sdk",
  "runtime-version": "3.26",
  "command": "frogr",
  "finish-args": [
    "--share=ipc", "--socket=x11",
    "--socket=wayland",
    "--share=network",
    "--filesystem=xdg-pictures"
  ],
  "build-options" : {
    "cflags": "-O2 -g",
    "env": {
      "V": "1"
    }
  },
  "cleanup": [ "/share/man" ],
  "modules": [
    {
      "name": "frogr",
      "buildsystem": "meson",
      "sources": [
        {
          "type": "git",
          "url": "git://git.gnome.org/frogr",
          "branch": "RELEASE_1.4",
          "commit": "e2322c8f99f9d3a3cdc020b79c6c7224ad1988d0"
        }
      ]
    }
  ]
}

Starting in flatpak-builder 0.10.10 you can now also use YAML for manifests. In term of the content model, YAML is a superset of JSON, but it is easier for humans to read and write. This makes it a good fit for flatpak-builder. In fact, the implementation internally just converts the YAML parser nodes to JSON parser nodes.

Yesterday I enabled support in flathub, so you can now convert existing apps, or submit new apps using YAML.

Here is how the above manifest looks in YAML:

id: org.gnome.frogr
runtime: org.gnome.Platform
sdk: org.gnome.Sdk
runtime-version: 3.26
command: frogr
finish-args:
  - --share=ipc
  - --socket=x11
  - --socket=wayland
  - --share=network
  # Grant app access to ~/Pictures
  - --filesystem=xdg-pictures
build-options:
  cflags: -O2 -g
  env: { V: '1' }
cleanup: [ /share/man ]
modules:
  - name: frogr
    buildsystem: meson
    sources:
      - type: git
        url: git://git.gnome.org/frogr
        branch: RELEASE_1.4
        commit: e2322c8f99f9d3a3cdc020b79c6c7224ad1988d0

This is clearly the same content as above (except it has a comment), but shorter and more readable.

For a longer example, see this conversion of a flathub app.

Of course, YAML is not perfect. Its a pretty complex format, it relies on indentation, and the specification has historically been a bit vague with implementations sometimes differing in interpretation. Going forward both JSON and YAML will be supported (and can even be mixed when you use includes), allowing you to chose what works best for you.

Flatpak inception

One interesting usecase of flatpak is as a compliment to the ideas of Fedora Atomic Workstation and similar projects. In other words, a read-only core image for the base operating system, and then using various types of containers and sandboxes for the applications on top of that.

One problem in such a setup is doing development, in that the basic core rarely contains development tools. This is helped a bit by flatpak using runtimes and SDKs, because the compiler used during the build is not from the host. However, flatpaks are typically built using flatpak-builder, which still has some dependencies on the host, such as git/bzr/svn and strip. These pull in a lot of packages that you don’t want on a minimal core OS image.

So, how do we solve this?

We must go deeper!

Introducing org.flatpak.Builder, a flatpak:ed version of flatpak-builder, which only needs flatpak to be installed on the system, thus making it possible to build apps on systems with zero development tools:

$ flatpak install flathub org.flatpak.Builder
...
$ flatpak run org.flatpak.Builder build-dir org.flatpak.Builder.json 
...

Additionally, with recent versions of flatpak, if you add /var/lib/flatpak/exports/bin to PATH you can use the shorter:

$  org.flatpak.Builder build-dir org.flatpak.Builder.json

The future is one step closer!

Moving a portal


Portals are a fundamental concept in flatpak. They are the way a sandboxed application can access information and services from the host in a safe, controlled way.

Most of the portals in use are implemented by a module called xdg-desktop-portal, with backend implementations for Gtk+ and KDE. Many of the portals in it, such as the important file chooser portal relies on a lowlevel portal called the document portal. It is a combined dbus and fuse service that controls access to files with fine-grained per-application permissions.

The snap developers are interested in using portals for snap packages, which is great for application developers as they only have to target a single API. However, historically the document portal was shipped as part of flatpak, which is suddenly a major problem.

To fix this we had to move the document portal from flatpak to xdg-desktop-portal, and I’m happy to announce that with todays releases of xdg-desktop-portal and flatpak we now achieved this.

Packagers need to be careful about this when updating to the new versions so that only one copy of the document portal is installed. The stable version of flatpak (0.10.4) can be built with or without the document portal, depending on what version of the desktop portal you have. The unstable flatpak release doesn’t have the document portal at all, and requires you to use the new desktop portal.

Fixing flatpak startup times

A lot of people have noticed that flatpak apps sometimes start very slowly. Upon closer inspection you notice this only happens the first time you run the application. Still, it gives a very poor first time impression.

So, what is causing this, and can we fix it?

The short answer to this is font-cache generation, and yes, I landed a fix today. For the longer version we have to take a detour into how flatpak and fontconfig works.

All flatpak applications use something called a runtime, which supplies the /usr that the application sees. This contains a copy of the fontconfig library, but also some basic fonts in /usr/share/fonts. However, since the /usr from the host is covered, the app cannot see the fonts installed on the host, which is not great.

To allow flatpak applications to use system fonts flatpak exposes a read-only copy of the host fonts in /run/host/fonts. The copy of fontconfig shipped in the runtime is configured to look for fonts in this location as well as /usr.

Loading font files scattered in a directory like this is very slow. You have to open each font file to get the details of it, like the name, so you can properly select fonts. To avoid this fontconfig has a font cache, which is generated each time a font is installed (using the fc-cache tool). Flatpak exposes these caches to the application too.

Unfortunately the format of the fontconfig cache is using the absolute filename as the cache key, which breaks when we relocate the font files from /usr/share/fonts to /run/host/fonts. This means that the first time an application starts it has to open all the file and generate its own cache (which is later reused, so it only affects the first launch).

It would be much better if the existing cache on the host could be re-used, even when the directory name is changed. I’ve been working on a fix for this which has recently landed in fontconfig (and is scheduled to be released as 2.13.0 soon).

Today I landed the fix for this in the standard flatpak runtimes, which, coupled with using the new fontconfig version on the host, dramatically reduce initial launch times. For example, launching gedit the first time on my machine goes from 2 seconds to 0.5 seconds.

All users should automatically get the runtime part of the the fix, but it will unfortunately take some time until all distributions have moved to the new fontconfig version. But once its there, this problem will be fixed for good.

Gnome apps migrated to flathub

Last week I finally migrated the last app from the gnome stable application flatpak repo to flathub. The old repo is now deprecated and will not get any new builds.

In the future, all stable flatpak builds of gnome apps will be on flathub only, so make sure you add it as a remote.

There are a lot of apps on flathub now, have a look. And if your application is not there but you’re interested in adding it, please check out the flathub docs.

On application sizes and bloat in flatpak

Flatpak is fundamentally a bundling system, and as such there are worries about bloat compared to non-bundling systems. Obviously there is some truth to this, as bundling do generally increase size. It is my opinion that the advantages of bundling offset these costs, however that is a discussion for another time. Today we’re going to look into what flatpak does to minimize bloat.

On the highest level, flatpak avoids duplication by having a runtime/application split. This means that applications don’t have to bundle everything, and that common dependencies are shared between applications.

However, runtimes were not primarily added in order to minimize bloat. Runtimes were added to have a way to do shared ownership, maintainership and releases of core stuff. Lowlevel things that application authors are not interested in nor experienced with, letting the applications focus on the application specific stuff, while still getting security updates of the core.

In fact, flatpak uses a system called ostree for the application storage which means, even without runtime sharing, a lot of duplication automatically go away.

When I try to explain what ostree is I always start with “Its like git, but for directories with large binaries”. This helps because most linux users these days have some knowledge about git. However, most people only know how to use git, and not its underlying data model. These details are important to understand when discussing ostree, so I will try to give a short explanation.

Lets start by looking at a simple ostree repository, with a single branch called “master”, containing a single file “a-file.txt”.  (Note: I manually shortened all IDs to 8 characters below to make it more readable)

Here is how the repository looks on disk:

├── config
├── objects
│   ├── 03
│   │   └── 02f468.file
│   ├── 7a
│   │   └── f088f5.commit
│   ├── 3e
│   │   └── ca26e9.dirtree
│   └── d5
│       └── 85282a.dirmeta
└── refs
    └── heads
        └── master

Pretty weird eh? Lets explore it a bit. The adventure starts with looking up the ref for the branch master:

$ cat repo/refs/heads/master
7af088f5

This references the id of the commit object that the master branch currently points to. The commit object info can be found in objects/7a/f088f5.commit. This is a binary file, but the contents are something like:

Root metadata: d585282a
Root files: 3eca26e9
Committer: Alex
Commit date: ...

The root metadata is a reference to objects/d5/85282a.dirmeta, which contains uid/gid/permissions for the root directory.

The root files contains a list of file and directory names and their object ids. In the case of subdirectories that would be a .dirtree object, but in this case there is only a file, 0302f468, which contains the actual file data:

$ cat repo/objects/03/02f468.file
a-file contents

So, we understand how to interpret the repository data, but it still seems weird. What are all those long ids for, and whats the reason for the whole thing? It turns out that the ids are the main point. They are in fact sha256 checksums of the object contents.

Whenever ostree commits a file it computes the checksum of the file and uses that as the id of the object in the repository. This means that if for whatever reason two files with identical contents are committed to the same repo, then they will use the same object. And this continues to directories too, if all the files and directories in two dirtree objects are identical (and have the same names) then the same dirtree object will be used.

In the example repo above, suppose that another branch was added, which contained the same file, plus some other file. Only the commit, the new file and the new dirtree for the root would be added, all the other objects would be reused.

What about flatpak?

This ostree feature is very interesting, because in flatpak  each installed runtime and application is a branch in a single ostree repository (combined with a checked out version that has hard-links to the files in the repository). So, any files that are identical are automatically shared between applications.

It should be noted that this sharing does not only affect disk space use. Since shared files are hard-linked, any RAM used for disk cache and memory mappings are automatically shared too. Additionally, when applications are downloaded we don’t have to download any objects that are already locally available, so network bandwidth is shared too.

How well does this work in practice? One way to look at this is comparing the gnome and the freedesktop runtimes. The gnome runtime is based on the freedesktop runtime, starting with it a copy of it, removing some files and then adding some.

We can check the size of the individual runtimes:

$ du -sh org.gnome.Platform/x86_64/3.26
665M org.gnome.Platform/x86_64/3.26
$ du -sh org.freedesktop.Platform/x86_64/1.6
435M org.freedesktop.Platform/x86_64/1.6

But we can also see how much space they use together, which takes into account files that are just hard-linked copies of each other:

$ du -sh org.gnome.Platform/x86_64/3.26 \
        org.freedesktop.Platform/x86_64/1.6
665M org.gnome.Platform/x86_64/3.26
18M org.freedesktop.Platform/x86_64/1.6

Here we see that installing both runtimes just adds an extra 18M due to the shared files. Similar measurements are true for the KDE runtime, which is based on the same freedesktop runtime build. And since they use the same base the gnome and KDE runtimes share this space too.

The above example is the ideal case for sharing, but even in unrelated builds this helps a lot. For example, let us compare the x86-64 and i386 builds of the gnome runtime:

$ du -sh org.gnome.Platform/i386/3.26
682M org.gnome.Platform/i386/3.26
$ du -sh org.gnome.Platform/x86_64/3.26
665M org.gnome.Platform/x86_64/3.26

$ du -sh org.gnome.Platform/x86_64/3.26 \
         org.gnome.Platform/i386/3.26
665M org.gnome.Platform/x86_64/3.26
462M org.gnome.Platform/i386/3.26

Here we saved 220M in two completely unrelated builds. This is caused by things like fonts, icons, and app data being the identical in the two builds.

Thats all for todays deep dive into flatpak internals. I hope you found this interesting.

Spotify and Skype flatpaks moved to flathub

This is a public service announcement.

I used to maintain two custom repositories of flatpaks for spotify and skype. These are now at flathub (in addition to a lot of other apps), and if you were using the old repository you should switch to the new one to continue getting updates.

This is easiest done by removing the current version and then following the directions on the flathub site for installing.

Maintaining a flatpak repository

So, you’ve built a flatpak, using flatpak-builder, and now you have a directory called repo. How do you go from here to something that your users can install the  application from?

To start with, lets make a minimal sample application, just so that we have a repo to look at:

$ echo $'#!/bin/sh\necho hello world' > hello.sh
$ flatpak build-init appdir com.example.App \
          org.freedesktop.Sdk \
          org.freedesktop.Platform 1.4
$ flatpak build appdir mkdir /app/bin
$ flatpak build appdir install --mode=750 hello.sh /app/bin
$ flatpak build-finish --command=hello.sh appdir

This produces the following appdir directory:

├── export
├── files/bin/hello.sh
└── metadata

We can now export this to the repo directory:

flatpak build-export repo appdir stable

If we copy this directory to a webserver, so that it is accessible via http, lets say as http://example.com/repo, then anyone can install your application like this:

flatpak remote-add --no-gpg-verify example \
        http://example.com/repo
flatpak install example com.example.App

Note: You can of course add a local remote using the path of the repo instead of a uri if you just want to test your build.

The anatomy of a repo

So, how does this work? Lets look a bit deeper at the repository. It
looks something like this:

├── config
├── objects
│ ├── 44/6a0ef11b7cc167f3b...0b6c5488.dirmeta
│ ├── 6e/340b9cffb37a989ca...617afa01d.dirtree
│ ├── 74/cfc6e8dd69905a525...64fc0d5e5.dirtree
│ ├── 75/af15131ec214e5074...41858318a.commit
│ ├── c4/083227ca305c44cd6...ceeecd27c.dirtree
│ ├── d8/fdc0a16351fa8bcd8...ac90e63e2.filez
│ ├── e4/2aa65aae7c7b61539...5557cad40.filez
│ └── e9/c16c731270e0a8718...cc8be2601.dirtree
├── refs/heads/app/com.example.App/x86_64/stable
└── summary

If you’ve ever seen a git repository, this looks very similar. The application is referred to by what is called a ref, in this case app/com.example.App/x86_64/stable. If we look at the file with that name in the refs/heads directory we get latest commit id:

$ cat repo/refs/heads/app/com.example.App/x86_64/stable
75af15131ec214e5074...41858318a

When flatpak installs the app it starts by pulling from the repository, which means we download into a local repository the corresponding commit object (i.e. objects/75/af15131ec214e5074...41858318a.commit) and then follow that until all required objects are downloaded.

Another important feature is the summary file. This is a single file that contains information from all the refs files in the repository. It is regenerated from the refs files each time you export a new app or run flatpak build-update-repo. This file is needed because when the repository is accessed via http there is no standard way to list all the refs files, for instance when listing available apps.

This is basically all you have to do to get something running, just build it and copy the results to a webserver. However, there are some details that get important when you’re maintaining a production repository.

GPG signatures

For an official build you want to have a GPG key to sign the repository. In ostree (which is what flatpak uses) every commit is signed, as well as the summary file. When we pull from the repository the summary and the commit signatures are verified, to make sure nothing was modified. This is important because it allows us to use unencrypted http for faster download, and it allows secure use of mirrored repos.

The example repo above did not have signatures, so lets create some. If you have a GPG key already you can use it, although it is recommended that you create a custom key for the repository. That way you can transfer maintainership without having to give anyone else your private key.

So, lets create a custom key:

$ mkdir gpg
$ gpg2 --homedir gpg --quick-gen-key user@example.com

This eventually create a keyring in the gpg directory and print it out:

pub rsa2048 2017-02-09 [S]
 770B194227ED91BF6C9038B83F4BF10E09A00F3B
 uid [ultimate] user@example.com
 sub rsa2048 2017-02-09 []

We can then sign the repo after the fact (you can also sign it at build-export time):

$ flatpak build-sign repo \
  --gpg-sign=770B194227ED91BF6C9038B83F4BF10E09A00F3B \
  --gpg-homedir=gpg

This will create the signature of the commit:

repo/objects/75/af15131ec214e5074...41858318a.commitmeta

You also need to sign the summary file:

$ flatpak build-update-repo repo                     \
 --gpg-sign=770B194227ED91BF6C9038B83F4BF10E09A00F3B \
 --gpg-homedir=gpg

Which will recreate the summary file, now with a summary.sig file next to it.

If we export the public key for the repo we can give it to users:

$ gpg2 --homedir=gpg \
  --export 770B194227ED91BF6C9038B83F4BF10E09A00F3B > example.gpg

And you can then use this instead of --no-gpg-verify when adding the remote:

$ flatpak remote-add --gpg-import=example.gpg \
         example http://example.com/repo

Flatpakref files

Having to both add a remote (with the right gpg key file) and then install the application is not a great user experience. To make this easier for users, flatpak has something called flatpakref files. These contain both the information about the repository, including the GPG key, and the application id. The result is that you can just point flatpak at one file and it will install the application.

Lets create a example-app.flatpakref file for our app:

[Flatpak Ref]
Title=Example App
Name=com.example.App
Branch=stable
Url=http://example.com/repo
GPGKey=mQENBFicjMMBCAC...
IsRuntime=false
RuntimeRepo=https://sdk.gnome.org/gnome.flatpakrepo

The GPGKey field is just a base64 version of the key, which you can get like this:

$ base64 example.gpg | tr -d '\n'

Most of the fields are obvious, but the last one need a special explanation. It specifies a file that describes the repository containing the runtimes that the application uses. If this repository is not configured on the users system it will be automatically added and the runtimes will be installed from it as needed.

Once the flatpakref file is put on the webpage your application can be installed in a single command:

flatpak install http://example.com/example-app.flatpakref

The user can also click on the flatpakref link, and it will open in the default application for software installation (like gnome-software). There are also some additional fields that will be shown in gnome-software: Homepage, Comment, Description and Icon.

Static deltas

When downloading an application, flatpak will do individual requests for each file in the application. If you have a lot of small files this can be slow, so you want to make sure the HTTP keep-alive is enabled on the web server. But even then it can be slow. Additionally, as objects are whole files, we still have to download the entire file even if just one byte in the file changed.

In order to solve this flatpak supports something called static deltas. These are pre-generated delta files (using bsdiff) that contain all the data needed to go from one version to another (or from nothing to a version). If these files are available they are used instead of the individual objects, which allows pulls to be much more efficient.

You can generate static deltas for the latest versions of all apps by passing --generate-static-deltas to flatpak build-update-repo, and I recommend everyone do this for production repositories.

Configuration

There are a couple of configurations in a flatpak repo that you can set via build-update-repo. One is the title (--title=TITLE). This will be used by default as the title when listing the configured remotes.

Another is the default branch (--default-branch=BRANCH). If this is set, then this will be used as the default branch name (the last part of the ref) when it is not specified. This is useful when you have multiple versions of the same app (for instance a nightly build and a stable build) in the same repository.

AppStream branches

In order to support nice graphical frontends like gnome-software, flatpak uses a metadata format called AppStream. The way this works is that each application ships an AppData xml file, and an icon. Then when you run flatpak build-update-repo, each such xml file and icon are extracted and put in a separate per-arch appdata branch (called e.g. appstream/x86_64). Flatpak then mirrors this branch locally for each remote, and this data is used by gnome-software.

So, make sure your application ships an appdata file, and make sure you run build-update-repo to update the branches whenever an application changes.

Syncing updates

If you build a new version of your app on you will get an updated repository locally that has both the old build and the new build. You then need then to copy this updated repo to the webserver. Generally if you just copy the files over with e.g. rsync then things will work fine.

However, there are some race conditions if the new repo files are written at the same time as someone is pulling from the repository. For instance, if you write the summary file before all the objects are copied, then there is a short period where a new pull will not see a complete version of the commit. Another race is if you delete objects from an older version before the new commit is fully uploaded.

All these races are easily avoided by ordering the sync so that the summary file is uploaded after all the objects, and any deletes are done at the end. The ostree-releng-scripts repo has a script to do this.

Hosting

Many developers don’t run their own server to host things on, relying instead on services like github or gitlab to host releases. Unfortunately these are generally designed to host single-file releases (such as tarballs), rather than a multi-file repository.

Since flatpak repositories are just a bunch of static files using Amazon S3, or similar services is very effective. I personally use S3 for the Skype and Spotify flatpak repos, and the cost is about $0.01 a month so far. So, in practice this is essentially free. However, you still need to register with Amazon and have a credit card.

I haven’t found any perfect completely free alternative, but there are some workarounds:

On github, you can use the github pages feature. To do this you create a new git repo and then you commit the flatpak repository into the git repo. Then you enable github pages for the repo and point it at the master branch.

Here is an example of using this: https://github.com/alexlarsson/test-releases
With the flatpak repo itself available as: https://alexlarsson.github.io/test-releases/repo

The maximum size of a github pages size is 1GB, and there is a soft bandwidth limit at 100GB per month, so depending on the size of your application and how popular it is, this could be a useful approach.

On gitlab you can use the gitlab pages feature to do something similar.