AppImages are my personal favourite cross-distribution packaging format, this is because unlike most packaging formats they are compact (which is something that binary tarballs lack, when they are decompressed in order to be run, that is) and do not need root privileges in order to be run. As with many other cross-distribution packaging formats, the fact that many upstream projects have not adopted the format as the primary way they distribute their software in binary format for Linux, is a real disappointment and roadblock hindering its widespread adoption. In my own personal efforts to counter this problem I have decided to write this post documenting the basics of how to create your own AppImage. If you have questions you can ask me, although I am not an expert on this and as such I frequently ask “the master” (my term, not his, rofl) for help on GitHub. The master, in this case, is Simon Peter (@probonopd on GitHub), the person that created the AppImage package format.

This post will cover the following:

  • How to write a YAML file to build an AppImage
  • How to build an AppImage locally (although it will only cover how to do this on a Debian-based distribution like deepin, Linux Mint, Q4OS, Ubuntu, etc.)
  • How to automate an AppImage build using Travis CI

Background

The objective when building an AppImage is to create a directory, called an AppDir (usually with the folder name <APPNAME>.AppDir where <APPNAME> is the name of the application you are creating an AppImage for) with the following structure:

<FS>
AppRun
<APPNAME>.desktop
<APPNAME>.{png,svg,xpm,jpg}

The final file <APPNAME>.{png,svg,xpm,jpg} is whatever image file (in PNG, SVG, XPM, JPG formats, just to name a few possible formats) for the app that is referenced in the desktop configuration file. <FS> is the AppImage’s copy of the application’s file system, this is also known by the variable name $PREFIX. See every app installed via one’s package manager has a set of system directories that contain its files. In the case of AppImages this file system also includes extra file libraries and executables to ensure the program will work on whichever system it finds itself. Here is an example <FS> for the Code::Blocks AppImage, I provided a link to it instead of including it in this post because it is simply too large to fit in this post.

Usually <FS> is created by downloading Debian or RPM packages for a package providing <APPNAME> and all of its dependencies for a source distribution and extracting their contents (usually if Debian packages are used they are extracted using the dpkg-deb -x command) to the AppDir. See these packages contain the files they provide in the location they are installed on your live system. For example, if you downloaded a Code::Blocks Debian package and extracted its contents to a local directory (say for argument’s sake /home/fusion809) then you would expect to see an executable file at /home/fusion809/usr/bin/codeblocks, as when that Code::Blocks Debian package is installed it installs an executable file to /usr/bin/codeblocks on your file system. The source distribution ideally should be the oldest (especially with regard to the age of the software in its official repositories), yet still supported distribution, that has a suitable package one can build the AppImage from. As an AppImage will only work on distributions of the same age or newer than the source distribution used to build the AppImage. So if you build an AppImage using packages from CentOS 7 you should not expect it to run on CentOS 6, as CentOS 6 is older than the source distribution, CentOS 7.

Before I tell you some reasonable choices for a source distribution, I need to clarify some notation I am going to use. For the distributions listed later this will be my version notation:

  • Release n is the latest stable release. For Debian as of 20 November 2016 this would be version 8 (or Jessie).
  • Release n-1 is the second latest stable release. For Debian as of 20 November 2016 this is version 7 (or Wheezy).
  • Release n-2 is the third latest stable release. For Debian as of 20 November 2016 this is version 6 (Squeeze). Please note how in the above notation examples I did not include dot point releases. That is, technically 8.6 is the latest stable release of Debian as of 20 November 2016, but I dropped the dot point release .6 from that to give 8.

So some reasonable choices for a source distribution include:

  • CentOS n-2/n-1/n, as of 20 November 2016 this would be 5, 6 or 7.
  • Debian n-1/n, although an older system would be preferable to n.
  • Mageia n-1/n. I personally would not recommend using Mageia, as their repositories are fairly minimalistic, in terms of the incldued software. Although, if you can get it to work, so be it.
  • openSUSE n-1. The latest Leap release of this distro as of 20 November 2016 is 42 (or if you include dot point releases 42.2). So n-1 is 13 (or 13.2 to be precise). I would say n-2 as well, but only n-1 and n are presently officially supported by the openSUSE development/packaging team.
  • Ubuntu LTS n-2/n-1. So presently, as of 20 November 2016, this would be 12.04/14.04.

After a suitable AppDir has been created, one merely needs to run the command appimagetool on the AppDir.

Recipe

Recipes are shell scripts that automate the process of building an AppImage. That is, they create a suitable AppDir for building an AppImage and then builds it using appimagetool. They were once the recommended way of building AppImages in an easily reproducible way, but this has since changed since the introduction of YAMLs. Although, it is important to note, that if you wish to use RPM packages to build your AppImage you will still likely need to use a Recipe, along with a Dockerfile (for details of this see the Docker section of this post). YAMLs are essentially designed to reduce repetitiveness as most Recipes contain common lines of code to them, so by writing a generalized Recipe that is fed information by a YAML one cuts down on how much one has to type to build an AppImage. Recipes that @probonopd has written (a list can be found here) are designed to be built on a Travis CI build instance with the following characteristics (based on his .travis.yml):

sudo: required
dist: trusty
addons:
  apt:
    packages:
    - bsdtar
    - curl
    - zsync
    - squashfs-tools

so in other words, a build instance using the OS Ubuntu 14.04 (or Trusty), with bsdtar, curl, sudo, zsync and squashfs-tools available for usage by his Recipe. As such his Recipes are not designed to use RPM packages to build an AppImage. Although it is worthwhile noting that he does include a few Recipes with Dockerfiles. These Dockerfiles exist when his Recipe is to be run on a Red Hat-based system like CentOS. Why does he use Travis CI to build his AppImages? Well that is a question for a later section, specifically the Travis CI section.

YAML

YAML files are metadata files that can be read by a specialized type of Recipe script to provide it the specifics of how it should build your AppImage. The main Recipe script that should be used to build an AppImage from the data in a YAML is here. They are only to be used when Debian packages are going to be used to build the AppImage or binary archives are to be used. If you want to use RPM packages to build your AppImage I am afraid that you will need to see the next section Docker. YAML files used to build AppImages have the following general format:

app:      <APP>          # Name of the App; can be capitalized
binpatch: true           # Only specified as needed, most AppImages should not need this field
union:    true           # Ditto

ingredients:
  packages:                # Debian package(s) to be included in the final AppImage. Excl. the dependencies, these will be automatically downloaded!
    - <PACK1>
    - <PACK2>
    - <PACK3>
    - ...
  dist:    <DIST>      # packaging distribution codename. For example jessie for Debian 8, oldstable for Debian 7, etc.
  sources:                 # lines in /etc/apt/sources.list of your packaging distribution
    - <SOURCE1>
    - <SOURCE2>
    - <SOURCE3>
    - ...
  script:                   # Commands to be executed before (or perhaps even instead of) Debian binaries are downloaded.
    - <LINE1-1>
    - <LINE1-2>
    - <LINE1-3>
    - ...

script:                     # Commands to be run after Debian binaries are downloaded and extracted. Ideally there will not be any.
  - <LINE2-1>
  - <LINE2-2>
  - <LINE2-3>
  - ...

They are usually named <APPNAME>.yml, as this makes it easier to automate their build with Travis CI. It is important to note, however, that not all of these fields are mandatory. The binpatch and union fields are only needed in select cases when ommitting them causes errors, usually runtime errors. Please note that they are mutually-exclusive, that is, you can only use one and not both in the one YAML! The packages, dist and sources fields are only required if you are building your AppImage from Debian packages, if you are using binary archives you do not need them. See for example my atom-beta YAML. If you are running a Debian-based operating system (like Ubuntu) you can try building an AppImage from a YAML (as a means of testing it) by running:

user $  bash <(curl -sL https://github.com/probonopd/AppImages/raw/master/recipes/meta/Recipe) <APPNAME>.yml

although sometimes this will not work due to missing appdata XML in usr/share/metainfo/<APPNAME>.metadata.xml in <FS>. In this case you will get a warning message:

WARNING: AppStream upstream metadata is missing, please consider creating it
         in usr/share/metainfo/<APPNAME>.appdata.xml
         Please see https://www.freedesktop.org/software/appstream/docs/chap-Quickstart.html#sect-Quickstart-DesktopApps
         for more information.

At this stage you can either do as the warning message suggests and create the file usr/share/metainfo/<APPNAME>.appdata.xml in your <FS> or you may wish to instead use:

user $  bash <(curl -sL https://github.com/fusion809/AppImages/raw/master/recipes/meta/qbittorrent/Recipe) <APPNAME>.yml

as this modified Recipe will ignore this warning.

Docker

As previously mentioned the YAML approach to building an AppImage is only valid for AppImages built from binary archives or Debian packages. So when you would like to build an AppImage from RPM packages you are stuck to using Recipes and Dockerfiles. Although theoretically, you could get around the need for Dockerfiles by building them locally on a system using the RPM package manager. If you used this technique, however, you could not use these Recipes to build an AppImage with Travis CI as Travis CI only supports the use of Ubuntu 12.04 and 14.04 build environments. If you would like to see some example Recipe and Dockerfile combinations that do this see:

It is notable that many (if not most) of these Recipes do not just build the AppImage from RPM packages but they also build some of the components of the AppImage from source code. Most Docker and Recipe combinations that I have seen use CentOS 6 as the base Docker image. This is why so many components of the AppImage has to be built from source code, as CentOS 6 usually includes very old libraries, executables, etc. and some programs require newer versions of these libraries and executables to be present in order for them to be build successfully. Usually the Dockerfile has the layout:

FROM centos:6
ADD https://github.com/<USER>/<REPO>/raw/master/recipes/<RECIPE>/Recipe /Recipe
RUN sed -i -e 's|sudo ||g' Recipe && bash -ex Recipe && yum clean all && rm -rf /dependencies && rm -rf /out && rm -rf /AppImage* && rm -rf /AppDir

where <RECIPE> is the name of the Recipe, <USER> is the GitHub username for the repo who’s Recipe we are using and <REPO> is the name of the repo in which the Recipe is.