{
  "$type": "site.standard.document",
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreicqr4s7hquemrisv2myg3wucollvdqpojc3n7c4i43ifuexcixjsu"
    },
    "mimeType": "image/png",
    "size": 66901
  },
  "description": "Lifecycle management for your AppImages.",
  "path": "/blog/2026-06-20-appherder/",
  "publishedAt": "2026-06-20T00:00:00.000Z",
  "site": "at://did:plc:zntngpowgd6rorjt3haywj36/site.standard.publication/3m36bacisus2x",
  "tags": [
    "appimage",
    "appherder",
    "linux",
    "systemd",
    "go",
    "cli",
    "package-management"
  ],
  "textContent": "Linux has many ways to install an app, which is a polite way of saying nobody agrees on one.\n\nFlatpak, Nix, Homebrew, distro packages, and AppImages all have their place in theory, but in practice you are not choosing a format. You are choosing an app, and the app shows up in whatever format the author and your distribution chose to make it available. Sometimes, that format sucks.\n\n---\n\n\n\nI'm going to focus on AppImages here because they occupy a particularly annoying middle ground in this crowded field. They are not quite packages, not quite portable apps, and not quite installers. An AppImage is basically an executable runtime with a compressed filesystem glued onto it. That filesystem contains the application, its libraries, and usually a .desktop file and icon. When you run the AppImage, the runtime mounts or extracts that payload and launches the app inside it.\n\nThey are executable filesystem bundles with just enough desktop-adjacent metadata to look like apps, but not enough glue to behave like installed software. That's a clever trick, but it also explains most of the weirdness.\n\nThe appeal is obvious. No root access needed, no distro-specific package, and no waiting for a distro maintainer to package your app. For self-distributing developers, AppImages are extremely convenient: build one file, throw it at users, and avoid most of the infrastructure and politics around app stores and distro repositories.\n\nThe catch is that running self-contained apps and installing them are not the same thing. An app is not installed just because your kernel agrees to execute it.\n\nUsers have higher expectations. They expect installed apps to:\n\nshow up in the app launcher\nhave the right icon\nopen files they support\nupdate without sending you spelunking through GitHub releases\nuninstall without leaving a sad little .desktop file pointing at something that no longer exists\n\nOn their own, AppImages do not provide that. They do not reliably integrate with your desktop. They do not reliably update themselves. Many are unsigned. There is no central registry. There may be embedded update info, or there may not. The payload might be SquashFS, DwarFS, or some hitherto unknown format. Some tools eventually fall back to executing the AppImage itself with --appimage-extract, which is a pretty funny thing to do to a file you are trying to inspect safely.\n\nTo be fair to AppImages, every other platform has goofy app formats, too.\n\nmacOS DMGs make you drag an icon into another icon in a goofy little ritual, but at least the app lands somewhere obvious and everything else is handled for you. Portable Windows EXEs are loose files with no real promises, though nobody is pretending otherwise. Anything more complicated or permanent needs its own bespoke wizard, which is something Linux has mostly avoided.\n\nThat said, I don't particularly like AppImages. But they solve a genuine problem and fill a small niche that traditional package managers and modern stores can't. AppImages offer clever dependency management and app distribution without the adult supervision of Flatpak or extended timelines of distro packages. That means no built-in sandboxing, no vetting, and no centralized rules, but that's kind of the point. It's the wild west of package formats.\n\nAnd like any wild west, the freedom cuts both ways. AppImages are censorship-resistant because distribution is fractured, decentralized, and piecemeal. That same chaos enables a great deal of laziness and danger. Tradeoffs abound.\n\n---\n\nAppHerder\n\nI was recently stuck on a hopelessly delayed plane for over three hours longer than planned. With no internet and too much time, I wrote the first version of AppHerder: a Python script doing its best to bring order to the chaos of my AppImages.\n\nSince then, I've dedicated a few nights to producing a more refined version in Go, available as an AppImage on GitHub.\n\nAppHerder is a local lifecycle manager for AppImages. It does not try to be an app store. It does not solve discovery. It does not decide what software should exist. It's barely a package manager. It just takes the AppImages you already have and makes them behave as you'd expect.\n\nHere's the gist: ~/AppImages becomes the source of truth, just like /Applications on macOS. Throw an AppImage in there and AppHerder adopts it. It reads the metadata, installs the icon, writes a patched .desktop file, and marks that launcher as its own with X-AppHerder=true. Delete the AppImage later and AppHerder cleans up the launcher, icon, and metadata it created.\n\nThe trick that makes this feel automatic is a systemd user path unit that watches ~/AppImages. When that directory changes, systemd runs appherder sync as a one-shot user service. There is no always-on daemon sitting around waiting.\n\nTo the user, this is basically transparent. Drop an app in the folder and within seconds it's in your menu. Delete it and it disappears just as fast.\n\n\n\nSince this is automatic rather than manually invoked, the systemd unit it triggers is relatively locked down. It runs with NoNewPrivileges, a private temp directory, a read-only system view, and explicit write access only to the directories AppHerder needs to manage. It is still your user account managing your files, not a sandbox, but it is tighter than a normal shell command.\n\nAppHerder uses the same systemd-user approach for automatic upgrades, with a timer instead of a path watcher. Because AppImages have no universal update mechanism, it is very easy to collect stale binaries that need to be manually replaced.\n\nLuckily, many AppImages provide basic update info in an embedded .upd_info section of the binary. AppHerder checks that first, then falls back to user-configurable sources when upstream metadata is missing or wrong.\n\nFor safer updates, AppImages can be signed to verify provenance. Not every AppImage is signed, because of course they aren't, but AppHerder enforces signatures for updates when available. If an AppImage is signed, AppHerder pins that signing key on first install and refuses unsigned or differently-signed updates after that. Unsigned apps still work, but they do not get to pretend they have the same provenance story as signed ones. It is up to the user to decide if they are comfortable using unsigned software.\n\n\n\nRegardless, AppHerder still verifies that the file is actually an AppImage and checks expected hashes when the update source provides them (spoiler: they do not always provide them). This verification happens before AppHerder installs the update.\n\nSome AppImage tools execute the AppImage with --appimage-extract to inspect its contents, but AppHerder unpacks them passively without execution. Yes, the user may eventually run the app, but that is a conscious user choice, not something happening automatically just because a manager wanted an icon. Technically speaking, this limits AppHerder to SquashFS and DwarFS AppImages, but that is the vast majority of all AppImages currently available.\n\nAnd because updates can always turn into \"oops, everything is worse now,\" AppHerder keeps several previous versions around for easy rollback.\n\n---\n\nAppHerder does not make AppImages perfect. They're not as tidy as Flatpaks or as vetted as your distro's packages. They are still AppImages. Sorry.\n\nInstead, it is an adversarial attempt at making the bargain a lot less bad. If the app you need ships as an AppImage, fine. Put it in ~/AppImages, let it behave like an app, and delete it when you're done. That should not leave you with a Downloads folder full of mystery binaries and desktop entries pointing at ghosts.\n\nYou can grab AppHerder from GitHub, or use the install script:\n\n\n\nIt downloads the latest AppImage, installs AppHerder into ~/AppImages, and enables automatic sync and upgrades. After that, ~/AppImages is the place: drop AppImages in there, delete them when you're done, and let AppHerder handle the boring parts.\n\nWith any luck, this makes them suck less. Regrettably, that may be as good as it gets.",
  "title": "Making AppImages suck less with AppHerder"
}