Introduction

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. Also, we include one conversely important AppImage by default, Wine (see Using Wine in Nitrux).

In today’s tutorial, we will make an AppImage file using a tool called appimage-builder. appimage-builder makes it very easy to create AppImages of your favorite applications. appimage-builder works by using files called 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 traditional 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.


Getting started

To make our AppImage for this tutorial, we will only need Docker. We won’t be compiling anything at all (of course, if you want to compile the program instead, you can go ahead and do that. I’d still recommend that you use a container, though). We will be using Docker to keep our root filesystem free of build and package dependencies (libraries, development libraries, headers, and what not). This will ensure that our builds can be reproducible every time (see Continous Integration) without braking or bloating our installation.

Since Docker is already installed in Nitrux, there’s no need to use the package manager. Please note that the Docker daemon is not started by default, but it’s pretty easy to start it, open Station, and run the following commands.

sudo cgroupfs-mount
sudo dockerd &

For this tutorial, we will make an AppImage of mpv the default multimedia player in Nitrux; for reference, mpv is a free (as in freedom) media player for the command line. It supports a wide variety of media file formats, audio and video codecs, and subtitle types.

Now that the daemon is started, we will be using the Docker container provided by the appimage-builder documentation. To create the container, run the following commands.

sudo docker pull appimagecrafters/appimage-builder:latest
sudo docker run --name appimage -it appimagecrafters/appimage-builder /bin/bash

Now that we’re in the container (which uses Ubuntu 18.04.5), we will create our working directory to put the recipe and the contents of the AppDir. For the sake of simplicity, I will create my structure as /builder/mpv/.

mkdir -p /builder/mpv/
cd /builder/mpv/
  • Please note that my directory structure is not mandatory; you can use your own conventions.

And we also need a text editor to use in the container; I will use nano.

Making a recipe

For appimage-builder to create our AppImage, we will need to provide a recipe. Using your de facto text editor, we can use one of the documentation examples as a template; here’s my version.

  • 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

If you notice there’s quite a lot of packages in the exclude section, that’s because I prefer to keep the AppImages that I generate relatively small. While this has the obvious benefit of a smaller file, it also means that the probability that the file works across multiple Linux distributions (from different families) is reduced, a.k.a, the file is less portable. Hence, 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.

  • 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 copy it to the container using Station. Create an empty file in our working directory using your de facto editor (I’m using nano), and copy the recipe and save it.

Generating the AppImage

To generate the AppImage using appimage-builder, all we need to do is run a single command.

appimage-builder --recipe recipe.yml --skip-tests
  • In the command above, my recipe name is recipe.yml; like before, you can name your recipe any way you want, then I tell appimage-builder not to perform the built-in tests as we will be testing the AppImage directly on our installation.

By this point, the AppImage will begin to be generated from the recipe.

  • To verify the AppImage file name, you can use the command ls -l.

Testing the AppImage

To test that the AppImage works, we will need to copy the AppImage file from the container to our host. First, we need to know the container ID of the container that we used. To find the container ID, run the following command.

sudo docker ps

The output of the command will look similar to this.

CONTAINER ID        IMAGE                               COMMAND             CREATED       
57293d54a61a        appimagecrafters/appimage-builder   "/bin/bash"         39 minutes ago

Since now we know our container ID and know our file name and path, we can copy it (to our home directory, for example).

sudo docker cp 57293d54a61a:/builder/mpv/mpv-0.32.0-1ubuntu1-x86_64.AppImage ~/
  • Please note that the file will be copied with root ownership (because while inside the container, you’re root, and we’re using Docker as root); you may want to change that before executing it.

Now we just run the AppImage.

And it works!.

Lastly, if you’d like, you can remove the container. To stop and remove the container, run the following commands.

sudo docker container stop 57293d54a61a
sudo docker container rm 57293d54a61a

Troubleshooting

In any given case that your AppImage doesn’t work, it may be, usually, 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.

Leave a Reply

© 2017-2020 Some Rights Reserved. Made with ♥ by Nitrux Latinoamericana S.C.