AppImage is one of many popular “image”-based packaging options for modern Linux distros, along with flatpak and snap. I chose to package my software project with AppImage because the software is written in Go with Wails. It produces a single binary, which I felt would require minimal packaging effort, and AppImage seemed like the easiest option. The binary still needs specific runtime libraries, and AppImage allows us to package these in the AppImage file, making it truly standalone. The best part is, modern tooling makes this process very straightforward.
First we’ll see how it all works, then I’ll describe an easy implementation of a build script used in my project 👉
Building AppImages
The modern ecosystem of AppImage tooling provides an essential tool, appimagetool
, that can be included in a build pipeline to produce an AppImage file from a basic directory structure.
Getting the tool
Downloading appimagetool
for your architecture is easy. Here’s how you can do it using curl
:
ARCH="x86_64"
APPIMAGETOOL="appimagetool-$ARCH.AppImage"
curl -L -o $APPIMAGETOOL https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$ARCH.AppImage
chmod +x $APPIMAGETOOL
Setting up the app directory
Now the tool can be used to create an AppImage file from an app directory. Setting up this directory is easy due to its minimal structure:
appdir/
AppRun
myapp.desktop
myapp.png
The app directory only requires three files:
An executable file named
AppRun
- This can be your application binary itself or a script that performs set up before executing your binary
A
.desktop
file in the Desktop Entry specification format, used by desktop environments on Linux to launch applicationsAn icon PNG file
Let’s explore the first two in more detail.
AppRun file
This file is the entry point into your package executed by the AppImage package structure. There are two approaches to providing this file:
- Use a Bash script to perform setup before executing your binary:
#!/bin/sh
cd "$(dirname "$0")"
# Setup here
exec ./myapp
This file needs to be made executable:
chmod +x appdir/AppRun
Next, simply move your binary into the directory so it will be executed by the script:
mv myapp appdir/myapp
- The other option is to rename your binary executable to
AppRun
and have it executed directly:
mv myapp appdir/AppRun
Desktop entry file
Desktop entry files have an INI-style format that looks like this:
[Desktop Entry]
Type=Application
Name=MyApp
Comment=The next big thing
Icon=myapp
Categories=Utility
The key-value pairings are self-explanatory. The important things to note are:
The
Icon
value must match the filename of your icon, i.e.myapp
refers tomyapp.png
in the directoryappimagetool
requires theCategories
key to be present. See a list of valid categories here
Check out this tutorial for a more in-depth look at these files.
Building the AppImage
With the tool downloaded and the app directory structure in place, producing an AppImage file requires only one command:
./$APPIMAGETOOL appdir
If the process succeeds, you will find the AppImage file in the current directory. The final directory set up would look like this (assuming a x86_64
architecture):
myapp-project/
appdir/
AppRun
myapp.desktop
myapp.png
appimagetool-x86_64.AppImage
MyApp-x86_64.AppImage
Finally, make the AppImage file executable and run it:
chmod +x MyApp-x86_64.AppImage
./MyApp-x86_64.AppImage
Makefile implementation
Makefiles may seem archaic to some, but I find they’re perfectly suited for declaring simple build pipelines like the one above. Here’s a Makefile I created for the Wails project:
ARCH ?= x86_64
BIN = build/bin/brainstack
APPIMAGETOOL = build/appimagetool-$(ARCH).AppImage
APPIMAGE = Brainstack-$(ARCH).AppImage
$(BIN):
wails build
appimage: build/$(APPIMAGE)
$(APPIMAGETOOL):
curl -L -o $(APPIMAGETOOL) https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$(ARCH).AppImage
chmod +x $(APPIMAGETOOL)
build/$(APPIMAGE): $(BIN) $(APPIMAGETOOL)
cp $(BIN) appdir/AppRun
./$(APPIMAGETOOL) appdir
chmod +x $(APPIMAGE)
mv $(APPIMAGE) build
.PHONY: appimage
Makefiles follow a simple paradigm. Rules with targets and prerequisites are declared like this:
targets: prerequisites
command
Let’s walk through what happens when we run the appimage
rule found in the above Makefile:
make appimage
The first thing to note is that the target of this rule is phony, as declared at the bottom of the Makefile. This means the target doesn’t represent a real file, so it will always run when called from the command-line like above. This is useful for high-level rules intended to be run by hand or from another script. When the appimage
rule runs, it checks its prerequisites to find that build/$(APPIMAGE)
doesn’t exist, so it in turn runs the matching rule:
build/$(APPIMAGE): $(BIN) $(APPIMAGETOOL)
cp $(BIN) appdir/AppRun
./$(APPIMAGETOOL) appdir
chmod +x $(APPIMAGE)
mv $(APPIMAGE) build
Because this rule depends on the binary built by Wails and the downloaded tool, those rules will be triggered to produce the requisite files. Lastly, the rule’s commands are executed, which copy the application binary into the app directory, run the appimagetool
to produce an AppImage file, make the file executable, and move the file into the build
directory to keep the project directory clean. It’s that easy!
You can learn more about Makefiles here.
Conclusion
Packaging software as AppImages is one of the easiest options available to us on modern Linux desktops. The simple build pipeline can be integrated into CI systems such as GitHub Actions to build and publish standalone AppImage files automatically when you release your software!
Check back for more articles on this topic as I explore it further.