A Rust implementation of AppImageUpdate - a tool for updating AppImages using efficient delta updates.
- Delta Updates - Only download the changed portions of the AppImage, saving bandwidth and time
- Parallel Updates - Update multiple AppImages concurrently with per-URL deduplication
- Multi-Forge Support - Fetch releases from GitHub, GitLab, Codeberg, and Gitea instances
- Decentralized - No central repository required; updates come directly from the source
- Directory Scanning - Pass a directory to update all AppImages inside
- Progress Bars - Real-time progress during downloads with per-file bars
- Checksum Verification - SHA1 verification ensures downloaded files are valid
- Permission Preservation - Maintains executable permissions from the original AppImage
- Skip Unnecessary Updates - Automatically skips update if the target file already exists with the correct checksum
- In-Place Updates - Updates to same filename preserve old version as
.oldbackup - Graphical Mode - Built-in file picker and progress dialog, with no GTK or Qt runtime dependency
Every release ships static binaries for x86_64, aarch64, riscv64, loongarch64, ppc64, and ppc64le on the releases page. An installed copy can upgrade itself with appimageupdate --self-update.
git clone https://github.com/pkgforge-dev/appimageupdate.git
cd appimageupdate
cargo install --path .appimageupdate ./myapp.AppImageappimageupdate ./app1.AppImage ./app2.AppImage ./app3.AppImageAppImages are updated concurrently. When multiple AppImages share the same update source, the download happens only once and the result is copied to the rest.
Control parallelism with -J:
appimageupdate -J 4 ~/Applications/Use 0 (default) to auto-detect based on CPU count.
appimageupdate ~/Applications/appimageupdate -j ./myapp.AppImageCheck multiple AppImages in parallel:
appimageupdate -j ~/Applications/Exit code 1 if any update available, 0 if all up to date.
appimageupdate -d ./myapp.AppImageappimageupdate -gAsks whether to update individual AppImages or everything in a folder, opens the matching picker, then shows a progress dialog. Passing paths alongside -g skips the picker and updates them directly, so appimageupdate -g ~/Applications/ goes straight to updating.
The GUI also opens on its own when appimageupdate is run with no AppImage, no terminal attached and a display server available, which is what happens when it is double-clicked in a file manager. Cron jobs, systemd units and CI runners have no display server, so they keep getting the usage error instead.
Cancel stops the update straight away, part way through a scan or download, and leaves the existing AppImage untouched.
-j, -d and -l report through stdout and the exit code, so they stay on the command line even when combined with -g.
The dialogs are drawn by zenity-rs directly on X11 or Wayland, so the binary stays self-contained. Build with --no-default-features to drop the GUI entirely.
appimageupdate [OPTIONS] [APPIMAGE]...
Arguments:
[APPIMAGE]... Path(s) to AppImage(s) or directories to update
Options:
-O, --overwrite Overwrite existing target file
-r, --remove-old Remove old AppImage after successful update
-u, --update-info <INFO> Override update information in the AppImage
--output-dir <DIR> Output directory for updated AppImages
-d, --describe Parse and describe AppImage and its update information
-g, --gui Update through a graphical file picker and progress dialog
-j, --check-for-update Check for update (exit 1 if any available, 0 if not)
-l, --list-releases List available releases from the update source
-t, --target-tag <TAG> Install a specific version (e.g., for downgrade)
-J, --jobs <N> Number of parallel jobs (default: 0 = auto-detect)
--github-api-proxy <URL> GitHub API proxy [env: GITHUB_API_PROXY]
--gitlab-api-proxy <URL> GitLab API proxy [env: GITLAB_API_PROXY]
--codeberg-api-proxy <URL> Codeberg API proxy [env: CODEBERG_API_PROXY]
-h, --help Print help
-V, --version Print version
- Reads Update Information - Extracts embedded update info from the AppImage's
.upd-infoELF section - Fetches Zsync Metadata - Downloads the
.zsynccontrol file which contains block checksums and file metadata - Calculates Delta - Compares local file blocks against remote blocks using rolling checksums
- Downloads Only Changes - Fetches only the blocks that differ from the local copy
- Assembles & Verifies - Reconstructs the file and verifies SHA1 checksum
| Format | Description |
|---|---|
zsync|<url> |
Direct zsync URL |
gh-releases-zsync|<owner>|<repo>|<tag>|<filename> |
GitHub releases |
gl-releases-zsync|<owner>|<repo>|<tag>|<filename> |
GitLab releases |
cb-releases-zsync|<owner>|<repo>|<tag>|<filename> |
Codeberg releases |
gitea-releases-zsync|<instance>|<owner>|<repo>|<tag>|<filename> |
Gitea releases |
forgejo-releases-zsync|<instance>|<owner>|<repo>|<tag>|<filename> |
Forgejo releases (alias for Gitea) |
The <tag> field supports special values:
latest- Latest stable (non-prerelease) releaselatest-pre- Latest prereleaselatest-all- Latest release regardless of prerelease status- Any other value is treated as a specific tag name
The <filename> field supports glob patterns (e.g., *x86_64.AppImage).
Create a config file at ~/.config/appimageupdate/config.toml:
# API proxies (supports single string or array for fallback)
github_api_proxy = "https://ghproxy.net"
# gitlab_api_proxy = "https://glproxy.example.com"
# codeberg_api_proxy = "https://cbproxy.example.com"
# Or multiple proxies for fallback:
# github_api_proxy = ["https://ghproxy.net", "https://mirror.example.com"]
# Remove old AppImage after successful update
remove_old = true
# Output directory for updated AppImages (supports shell expansion)
output_dir = "~/Applications"| Variable | Description |
|---|---|
GITHUB_TOKEN / GH_TOKEN |
GitHub authentication token |
GITLAB_TOKEN / GL_TOKEN |
GitLab authentication token |
CODEBERG_TOKEN |
Codeberg authentication token |
GITEA_TOKEN / FORGEJO_TOKEN |
Gitea/Forgejo authentication token |
GITHUB_API_PROXY |
GitHub API proxy URL (comma-separated for multiple) |
GITLAB_API_PROXY |
GitLab API proxy URL (comma-separated for multiple) |
CODEBERG_API_PROXY |
Codeberg API proxy URL (comma-separated for multiple) |
APPIMAGEUPDATE_REMOVE_OLD |
Set to true to remove old AppImage after update |
APPIMAGEUPDATE_OUTPUT_DIR |
Output directory for updated AppImages |
Priority: CLI args > Environment variables > Config file
This is a Rust rewrite of the upstream AppImageUpdate (C++/Qt).
Advantages:
- Single static binary with no runtime dependencies
- Over 10x smaller
- Multi-forge support (GitHub, GitLab, Codeberg, Gitea/Forgejo)
- Cleaner, more maintainable codebase
- URL caching to avoid redundant API calls
- Proxy support for all forges
- Better error messages
Differences:
- No GPG signature verification (not implemented)
- No pling integration (not implemented)
- GUI draws its own dialogs instead of requiring Qt
use appimageupdate::{Updater, Error};
fn main() -> Result<(), Error> {
let updater = Updater::new("./myapp.AppImage")?
.progress_callback(|done, total| {
if total > 0 {
eprintln!("Progress: {}/{}", done, total);
}
});
if updater.check_for_update()? {
let (path, stats) = updater.perform_update()?;
println!("Updated to: {}", path.display());
println!("Saved {}% bandwidth", stats.saved_percentage());
} else {
println!("Already up to date!");
}
Ok(())
}