AppImages are the focus of our Linux distribution. We already include several AppImage-related tools that improve their user experience in our distribution, from desktop integration to sandboxing and management.
In today’s tutorial, we will make an AppImage file using appimage-builder.
appimage-builder makes it very easy to create your favorite applications. appimage-builder works by using recipes; these are simple text files in the YML format that contain the information from which appimage-builder will make our AppImage.
One of the main features of appimage-builder is building an AppImage from existing, pre-compiled packages like Debian packages, RPM packages, etc. Currently, only Debian packages are supported; however, more package managers will be supported in the future, such as Pacman.
appimage-builder is developed by Alexis Lopez Zubieta, a former Nitrux developer and an AppImage collaborator.
Difficulty: ★★☆☆☆
📜 Table of Contents
Getting Started
To make our AppImage for this tutorial, we only need to use Distrobox. We won’t be compiling anything at all (of course, if you want to compile the program instead, you can do that). For this tutorial, we will make an AppImage of mpv; for reference, mpv is a free (as in freedom) media player for the command line.
- 🔰 Information: Check our tutorial to use Distrobox and create a container.
We strongly recommend installing the following version of appimage-builder. To do that, run the following command.
sudo pip3 install git+https://github.com/AppImageCrafters/appimage-builder.git
Now, we can follow the appimage-builder documentation, enter the container, and create our working directory to put the recipe and the contents of the AppDir. For the sake of simplicity, we will create our directory structure as /builder/mpv/.
mkdir -p /builder/mpv/ cd /builder/mpv/
- 🔰 Information: This directory structure is not mandatory; you can use your conventions.
And we also need a text editor to use in the container.
Making a Recipe
For appimage-builder to create our AppImage, we must provide a recipe. Using your de facto text editor, we can use one of the documentation examples as a template; here’s ours.
- 🔰 Information: Although the recipe is self-explanatory, please refer to the appimage-builder documentation to know more about what each section does.
version: 1 script: [] AppDir: path: ./AppDir app_info: id: mpv name: mpv icon: mpv version: 0.32.0-1ubuntu1 exec: usr/bin/mpv apt: arch: amd64 sources: - sourceline: 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu focal main restricted universe multiverse' - sourceline: 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu focal-security main restricted universe multiverse' - sourceline: 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu focal-updates main restricted universe multiverse' key_url: 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920D1991BC93C' include: - mpv - libdc1394-22 - libwbclient0 exclude: - adwaita-icon-theme - appmenu-gtk-module-common - appmenu-gtk2-module - appmenu-gtk3-module - baloo-kf5 - bolt - breeze - breeze-cursor-theme - breeze-icon-theme - bubblewrap - cpp - cpp-9 - dconf-cli - dconf-gsettings-backend - dconf-service - dirmngr - dmidecode - drkonqi - e2fsprogs - fdisk - fuse - gamin - gcc-10-base - gcc-9-base - gdb - gdisk - gir1.2-atk-1.0 - gir1.2-freedesktop - gir1.2-gdkpixbuf-2.0 - gir1.2-glib-2.0 - gir1.2-gtk-3.0 - gir1.2-ibus-1.0 - gir1.2-pango-1.0 - glib-networking - glib-networking-common - glib-networking-services - gnupg - gnupg-l10n - gnupg-utils - gpg - gpg-agent - gpg-wks-client - gpg-wks-server - gpgconf - gpgsm - gpgv - gsettings-desktop-schemas - gtk-update-icon-cache - hicolor-icon-theme - humanity-icon-theme - hwdata - ibus - ibus-data - ibus-table - ibus-table-emoji - liba52-0.7.4 - libaa1 - libacl1 - libaom0 - libapparmor1 - libappmenu-gtk2-parser0 - libappmenu-gtk3-parser0 - libappstream4 - libappstreamqt2 - libarchive13 - libaribb24-0 - libasn1-8-heimdal - libasound2 - libasound2-data - libasound2-plugins - libass9 - libassuan0 - libasyncns0 - libatasmart4 - libatk-bridge2.0-0 - libatk1.0-0 - libatk1.0-data - libatspi2.0-0 - libaudit-common - libaudit1 - libavahi-client3 - libavahi-common-data - libavahi-common3 - libavc1394-0 - libavcodec58 - libavformat58 - libavutil56 - libbabeltrace1 - libbasicusageenvironment1 - libblkid1 - libblockdev-fs2 - libblockdev-loop2 - libblockdev-part-err2 - libblockdev-part2 - libblockdev-swap2 - libblockdev-utils2 - libblockdev2 - libbluetooth3 - libbluray2 - libbrotli1 - libbsd0 - libbz2-1.0 - libcaca0 - libcairo-gobject2 - libcairo2 - libcanberra-pulse - libcanberra0 - libcap-ng0 - libcap2 - libcddb2 - libchromaprint1 - libcodec2-0.9 - libcolorcorrect5 - libcolord2 - libcom-err2 - libcrypt1 - libcups2 - libcurl3-gnutls - libdatrie1 - libdb5.3 - libdbus-1-3 - libdbusmenu-qt5-2 - libdca0 - libdconf1 - libdevmapper1.02.1 - libdmtx0b - libdouble-conversion3 - libdvbpsi10 - libdvdnav4 - libdvdread7 - libdw1 - libebml4v5 - libeditorconfig0 - libelf1 - libepoxy0 - libevdev2 - libevent-2.1-7 - libexpat1 - libext2fs2 - libfaad2 - libfdisk1 - libffi7 - libflac8 - libfontenc1 - libfribidi0 - libfuse2 - libgamin0 - libgcrypt20 - libgdbm-compat4 - libgdbm6 - libgdk-pixbuf2.0-0 - libgdk-pixbuf2.0-common - libgirepository-1.0-1 - libgit2-28 - libglib2.0-0 - libglib2.0-bin - libglib2.0-data - libgme0 - libgmp10 - libgnutls30 - libgomp1 - libgpg-error0 - libgpgme11 - libgpgmepp6 - libgpm2 - libgps26 - libgraphite2-3 - libgroupsock8 - libgsm1 - libgssapi-krb5-2 - libgssapi3-heimdal - libgstreamer-plugins-base1.0-0 - libgstreamer1.0-0 - libgtk-3-0 - libgtk-3-common - libgudev-1.0-0 - libharfbuzz0b - libhcrypto4-heimdal - libheimbase1-heimdal - libheimntlm0-heimdal - libhogweed5 - libhttp-parser2.9 - libhunspell-1.7-0 - libhx509-5-heimdal - libibus-1.0-5 - libice6 - libicu66 - libidn11 - libidn2-0 - libinput10 - libisl22 - libixml10 - libjack-jackd2-0 - libjansson4 - libjbig0 - libjpeg-turbo8 - libjpeg8 - libjs-underscore - libjson-glib-1.0-0 - libjson-glib-1.0-common - libk5crypto3 - libkmod2 - libkrb5-26-heimdal - libkrb5-3 - libkrb5support0 - libksba8 - libkscreenlocker5 - liblcms2-2 - libldap-2.4-2 - libldap-common - liblirc-client0 - liblivemedia77 - liblmdb0 - libltdl7 - liblua5.2-0 - liblz4-1 - liblzma5 - libmad0 - libmatroska6v5 - libmbedcrypto3 - libmbedtls12 - libmbedx509-0 - libminizip1 - libmm-glib0 - libmount1 - libmp3lame0 - libmpc3 - libmpcdec6 - libmpdec2 - libmpeg2-4 - libmpfr6 - libmpg123-0 - libmtdev1 - libmtp-common - libmtp9 - libmysofa1 - libncurses6 - libncursesw6 - libndp0 - libnettle7 - libnewt0.52 - libnfs13 - libnghttp2-14 - libnl-3-200 - libnl-genl-3-200 - libnl-route-3-200 - libnm0 - libnotificationmanager1 - libnpth0 - libnspr4 - libnss3 - libnuma1 - libogg0 - libopenconnect5 - libopenjp2-7 - libopenmpt-modplug1 - libopenmpt0 - libopus0 - liborc-0.4-0 - libp11-kit0 - libpackagekitqt5-1 - libpam-modules - libpam-systemd - libpam0g - libpango-1.0-0 - libpangocairo-1.0-0 - libpangoft2-1.0-0 - libpangoxft-1.0-0 - libparted-fs-resize0 - libparted2 - libpci3 - libpcre2-16-0 - libpcre2-8-0 - libpcre3 - libpcsclite1 - libperl5.30 - libphonon4qt5-4 - libphonon4qt5-data - libpipewire-0.3-0 - libpipewire-0.3-modules - libpixman-1-0 - libplacebo7 - libplasma-geolocation-interface5 - libpng16-16 - libpopt0 - libpostproc55 - libprocesscore9 - libprocessui9 - libprotobuf-lite17 - libproxy1v5 - libpsl5 - libraw1394-11 - libre2-5 - libreadline8 - libresid-builder0c2a - librest-0.7-0 - libroken18-heimdal - librsvg2-2 - librsvg2-common - librtmp1 - libsamplerate0 - libsasl2-2 - libsasl2-modules-db - libscim8v5 - libsdl-image1.2 - libsdl1.2debian - libsecret-1-0 - libsecret-common - libselinux1 - libshine3 - libshout3 - libsidplay2 - libslang2 - libsm6 - libsmartcols1 - libsnapd-glib1 - libsnappy1v5 - libsndfile1 - libsndio7.0 - libsoup-gnome2.4-1 - libsoup2.4-1 - libsoxr0 - libspa-0.2-modules - libspatialaudio0 - libspeex1 - libspeexdsp1 - libsqlite3-0 - libsrt1 - libss2 - libssh-4 - libssh-gcrypt-4 - libssh2-1 - libssl1.1 - libstemmer0d - libstoken1 - libswresample3 - libswscale5 - libsystemd0 - libtag1v5 - libtag1v5-vanilla - libtaskmanager6 - libtasn1-6 - libtdb1 - libteamdctl0 - libthai-data - libthai0 - libtheora0 - libtiff5 - libtinfo6 - libtomcrypt1 - libtommath1 - libtss2-esys0 - libtwolame0 - libudev1 - libudisks2-0 - libunistring2 - libupnp13 - libusageenvironment3 - libusb-1.0-0 - libuuid1 - libva-drm2 - libva-wayland2 - libva-x11-2 - libva2 - libvdpau1 - libvlc5 - libvlccore9 - libvorbis0a - libvorbisenc2 - libvorbisfile3 - libvpx6 - libvulkan1 - libwacom-common - libwacom2 - libwavpack1 - libweather-ion7 - libwebp6 - libwebpdemux2 - libwebpmux3 - libwebrtc-audio-processing1 - libwind0-heimdal - libwrap0 - libx264-155 - libx265-179 - libxau6 - libxaw7 - libxdamage1 - libxext6 - libxfixes3 - libxft2 - libxi6 - libxinerama1 - libxkbcommon-x11-0 - libxkbcommon0 - libxkbfile1 - libxml2 - libxmu6 - libxmuu1 - libxpm4 - libxslt1.1 - libxss1 - libxt6 - libxtst6 - libxv1 - libxvidcore4 - libxxf86dga1 - libxxf86vm1 - libyaml-0-2 - libzstd1 - libzvbi-common - libzvbi0 - logsave - milou - mime-support - mobile-broadband-provider-info - ocl-icd-libopencl1 - oxygen-sounds - parted - pci.ids - pciutils - perl - perl-base - perl-modules-5.30 - pinentry-curses - pipewire - pipewire-bin - python3 - python3-gi - python3-ibus-1.0 - python3-minimal - python3.8 - python3.8-minimal - qtchooser - qtvirtualkeyboard-plugin - readline-common - sed - sound-theme-freedesktop - sudo - tpm-udev - tzdata - ubuntu-mono - udev - udisks2 - usb.ids - usbutils - vlc-data - vlc-plugin-base - vlc-plugin-video-output - wpasupplicant - x11-utils - x11-xserver-utils - xdg-desktop-portal - xdg-desktop-portal-kde - xkb-data - zlib1g runtime: env: PATH: $APPDIR/usr/bin:$PATH APPDIR_LIBRARY_PATH: $APPDIR/usr/lib/x86_64-linux-gnu/:$APPDIR/usr/lib/x86_64-linux-gnu/pulseaudio:$APPDIR/usr/lib/x86_64-linux-gnu/samba/ files: exclude: - usr/lib/x86_64-linux-gnu/gconv - usr/share/man - usr/share/doc/*/README.* - usr/share/doc/*/changelog.* - usr/share/doc/*/NEWS.* - usr/share/doc/*/TODO.* AppImage: update-information: None sign-key: None arch: x86_64
Many packages are in the “exclude” section because we prefer to keep the small AppImages we generate. While this has the apparent benefit of a smaller file, it also means that the probability that the file works across multiple Linux distributions (from different families and versions, i.e., fixed release distributions) is reduced; in other words, the AppImage file we generate will be less portable.
So, you have to keep that in mind.
To put it another way, everything you don’t put inside the AppImage is something you’re expecting to find in the target system, and vice-versa, i.e., desktop Linux distributions are likely to include the adwaita-icon-theme package, so that’s something that we can exclude from the final binary.
- 🔰 Information: In my recipe, I’m using Ubuntu repositories to tell appimage-builder from where to pull the packages. However, this is not mandatory; you can use any APT/dpkg repository (Debian, Devuan, Ubuntu, Trisquel, etc.), including Launchpad PPAs.
Now that we have a recipe for appimage-builder, we can build our AppImage.
Generating the AppImage
To generate the AppImage using appimage-builder, we only need to run a single command.
appimage-builder --recipe recipe.yml --skip-tests
- 🔰 Information: In the command above, our recipe name is recipe.yml; like before, you can name your recipe any way you want, then we tell appimage-builder not to perform the built-in tests (to save time).
The AppImage will begin to be generated from the recipe by this point.
Testing the AppImage
To test that the AppImage works, run it from Station or double-click it on Index.
Troubleshooting
If your AppImage doesn’t work, it may be because of one or two things. Either your AppImage is missing a library, or the AppRun runtime does not find the library path in the AppDir.
- In the first scenario, you will want to continue fine-tuning your recipe by adding packages to the section include (and removing them from the exclude section if you added them here).
- In the second scenario, you will want to check to add the library path to the section runtime.
If your recipe failed to generate an AppImage, it might be because of missing GPG keys, connectivity problems with the repositories used, or missing packages, i.e., the package doesn’t exist.
That’s it; this concludes today’s tutorial.