The AppImage File Format

This document records the AppImage format as specified by the AppImage project, the payload layout the reader depends on, and the byte-level detection rules.

Live-state facts in this document were verified on 2026-09-21 against the sources listed below.

Sources

Definitions

Image Formats

The specification defines three image types, distinguished by a three-byte magic value at offset 8 of the file.

Type Magic at offset 8 Filesystem Metadata carrier
0 none unspecified reserved for non-compliant files
1 0x41 0x49 0x01 (AI\x01) ISO 9660 with Rock Ridge ISO 9660 Volume Descriptor at offset 33651
2 0x41 0x49 0x02 (AI\x02) SquashFS appended to the ELF ELF sections .upd_info and .sha256_sig

Both type 1 and type 2 are valid ELF executables. The magic occupies bytes 8 to 10, which are part of the ELF e_ident padding field.

Type 1 rules

Type 2 rules

Payload offset for type 2

The runtime computes the payload offset as the size of the ELF part, using only the ELF header and the last section header.

sht_end          = e_shoff + (e_shentsize * e_shnum)
    last_section_end = sh_offset + sh_size          # of the section header at index e_shnum - 1
    elf_size         = max(sht_end, last_section_end)
    payload_offset   = elf_size

The algorithm selects the 32-bit or 64-bit ELF header from e_ident[EI_CLASS], converts multi-byte fields from the file byte order selected by e_ident[EI_DATA], and reads only the final section header rather than the whole table. The payload size is the file size minus payload_offset.

Contents of the Image

The payload filesystem is an AppDir and carries a fixed set of members.

Path Rule Purpose
AppRun must exist and be executable entry point the runtime executes
$APPNAME.desktop should exist, exactly one, at the root desktop entry for the payload
.DirIcon must exist icon per the AppDir specification, should be a 256x256 PNG
$APPICON.svg, $APPICON.svgz, or $APPICON.png may exist at the root fallback icon named by the Icon= key
usr/share/icons/hicolor/... should exist themed icons named by the Icon= key
usr/share/metainfo/$ID.appdata.xml should exist AppStream metadata

Embedded Metadata

Update information

Type 2 AppImages store update information in the ELF section .upd_info. The content is an ASCII string; a known transport mechanism is required, otherwise it should be empty or ignored.

Transport Form
zsync zsync\|https://server.domain/path/Application-latest-x86_64.AppImage.zsync
gh-releases-zsync gh-releases-zsync\|user\|repo\|tag\|filename.zsync
pling-v1-zsync pling-v1-zsync\|product-id\|pattern
bintray-zsync | the deprecated Bintray form; Bintray was shut down in 2021 |

The GitHub release tag accepts the special values latest (the latest release), latest-pre (the latest prerelease) and latest-all (the latest of either); any other value is a release tag. GitHub has no latest-prerelease endpoint, so this project reads the release list for those two and takes the first entry that fits, newest first.

pling-v1-zsync names a Pling product and a file-name pattern rather than a zsync file: Pling does not host the .zsync, so the transport needs Pling’s own API. This project recognises it and says that it cannot be checked, rather than pretending it can.

The value is a claim by the file about where its successors live. Reading it is free; following it means asking a third party for a download, so this project keeps the two apart: it resolves the string into the URL it names (appimage-inspect --update-url), asks that URL what it has (appimage-integrate update --check), and only replaces the file when asked to (appimage-integrate update). The replacement is verified first: the release’s published -SHA256.txt when the release has one, and always the downloaded file’s own .sha256_sig, whose PGP form is the strongest evidence available. A mismatch refuses the update. The zsync file named by the field is the AppImage’s own name plus .zsync, so the AppImage asset is the same pattern without that suffix.

The section is fixed size, so the string is followed by NUL padding, and some builds put non-text bytes there: the specification says content that is not a known transport should be empty or ignored. This project trims the padding, reports what is left when it is printable text, and ignores anything else. Two consequences are visible on this host: the four FreeCAD AppImages carry a real gh-releases-zsync|… value that was previously invisible, and the two Cura AppImages carry the literal string guess, which is an appimagetool option rather than an update information value and is reported as it stands so the owner can see it.

Signature

Type 2 AppImages may store a signature in the ELF section .sha256_sig. The signature covers the SHA-256 digest of the AppImage with the .sha256_sig section replaced by zero padding.

This project checks that section when it holds something to check:

Content What happens
absent, or zero padding nothing to check
64 hexadecimal digits, optionally prefixed sha256: the digest of the file with the section zeroed is computed and compared
an ASCII-armoured PGP signature the computed digest is passed to gpg --verify against the signature; a missing gpg or an unknown key is reported as not verified, and only a bad signature is a mismatch
anything else reported as present, unrecognised

explain, plan, appimage-inspect and install report the result; a definite mismatch makes install refuse unless --ignore-signature is given. Checking a signed file hashes the whole file, which is why an unsigned file costs nothing.

The comparison assumes the section’s file offset and size are read correctly. Modern AppImages are 64-bit ELF, whose section header fields sit at different offsets from the 32-bit form; reading a 64-bit file with the 32-bit offsets yields zero for both, so the section looks absent. That was a real defect here until a signature test exposed it.

Payload Filesystem: SquashFS

The type 2 payload is a SquashFS image. Its superblock begins at payload_offset.

Field Width Notes
s_magic 4 0x73717368, the bytes hsqs in little-endian order
inodes 4 inode count
mkfs_time 4 creation timestamp
block_size 4 data block size, commonly 131072
fragments 4 fragment count
compression 2 compression identifier
block_log 2 log2 of block_size
flags 2 filesystem flags
no_ids 2 uid/gid table entries
s_major / s_minor 2 / 2 format version
root_inode 8 packed root inode reference
bytes_used 8 filesystem size in bytes
id_table_start 8 uid/gid lookup table start
xattr_id_table_start 8 extended attribute table start
inode_table_start 8 inode table start
directory_table_start 8 directory table start
fragment_table_start 8 fragment table start
lookup_table_start 8 export (NFS) table start

Compression identifiers in the compression field are:

Value Compression
1 gzip (zlib)
2 lzma
3 lzo
4 xz
5 lz4
6 zstd

Additional constants used by a reader:

The reference implementations read the payload through libsquashfuse rather than a private SquashFS implementation.

Desktop Integration

The software inside an AppImage may integrate with the host desktop, but it should ask the user first.

Desktop integration should be skipped when any of these hold:

A static runtime must support TARGET_APPIMAGE; when it names an existing path, the runtime uses that filesystem image instead of its own appended one.

Consequences for the Reader

The AppImage reader in this project parses the container natively and does not depend on libappimage.

File name conventions are recommendations, not requirements; the reader must not depend on the .AppImage extension.

Verification