A basic way to hotreload an AppImage
Repack and run every time you hit save
I have been going down a read-it-later app rabbit hole — Pocket, Instapaper, Wallabag, Readeck: I’ve tinkered with them all — and I have reached the depth where I start to hack on the software to make it work how I want.
At first, it was API scripts for Instapaper and Wallabag. Then, I patched the KOReader Wallabag plugin to add star support. Then, I tried Readeck, and tinkered with OPDS. Then, I ported my Wallabag patch over to the Readeck KOReader plugin.
The KOReader maintainers, understandably, wanted there to be a way to disable my lightly-tested feature.
It wrote the patches blind, loaded them onto my Kobo, and they worked first try. But writing UI code that way, in a language I’ve never used? Not likely!
I looked into setting up a KOReader development environment (in an emulator). But it turns out this isn’t necessary:
If you only want to work with Lua frontend stuff, you can grab the AppImage and run it with
–appimage-extract.
I know nothing about AppImages, and nothing about Lua; I even haven’t read a single tutorial. Let’s go, this sounds like fun!
I downloaded the latest KOReader AppImage from https://github.com/koreader/koreader/releases/latest.
Now I had koreader-appimage-ARCHITECTURE-vYYYY.MM.AppImage (in my case, koreader-appimage-x86_64-v2025.10.AppImage) in my working directory.
I ran ./koreader-appimage-x86_64-v2025.10.AppImage –appimage-extract
A directory appeared called squashfs-root, with KOReader things in it. Cool!
I dropped in my modified Readeck.koplugin:
cp -r path/to/readeck.koplugin/readeck.koplugin/ path/to/squashfs-root/plugins/
Now, how to sew the thing back up and run it?
With appimagetool, which is itself (fittingly) an AppImage.
I downloaded appimagetool-x86_64.AppImage and ran:
./appimagetool-x86_64.AppImage squashfs-root/
Boom! KOReader-x86_64.AppImage appeared, and when I ran it (./KOReader-x86_64.AppImage), a KOReader appeared, with the Readeck plugin I had added.
I got hacking on the plugin UI, but that’s a story for another post. Because first, I wanted to get an efficient development environment and shorten the feedback loop.
I often use the handy entr utility for hot-reloading. In this case it was slightly more complicated, for a few reasons:
- It was strangely hard to find a process ID to quit the old KOReader
- you can’t run
killall koreader, orpgrep -i koreader, orpkill –full -i koreadereven though there is a process with the short nameKOReader-x86_64running. You want the ./luajit process running reader.lua.
- you can’t run
- I made various dumb mistakes
- we won’t go into it
entrdoesn’t work with a build command that never exits (duh)- I thought I should be able to background the process with
./KOReader-x86_64.AppImage &but that didn’t work and I don’t really understand why. Redirecting all output, running it in the background, and disowning it did work.
- I thought I should be able to background the process with
Dependencies: awk, xargs, entr, fish (optional). Install ’em all on Debian-based distros (if you’re lucky) using:
sudo apt install awk xargs entr
The Script
Create a script called build-appimage.sh (or something smarter) with the following contents:
#!/bin/bash
# remove the old appimage (can't remember if this is needed)
rm KOReader-x86_64.AppImage
# Send SIGTERM to the right KOreader process
ps -ef | grep -i 'reader.lua' | awk {'print $2'} | xargs kill
# ^ this was half the work, because there are a bunch of different processes,
# and this is the one that will actually get rid of the running KOReader
# Pack the new AppImage
./appimagetool-x86_64.AppImage squashfs-root/
# Run it, then get free of its sticky fingers
./KOReader-x86_64.AppImage > /dev/null 2>&1 &
disown
(You may need to run chmod +x build-appimage.sh, and also possibly chmod +x the AppImages themselves.)
You probably want to also download the runtime(s) for the architectures you need from https://github.com/AppImage/type2-runtime/releases, and pass them to appimagetool so they don’t get downloaded every time it builds. Like this:
./appimagetool-x86_64.AppImage --runtime-file="runtime-x86_64" squashfs-root/
Then, in Fish Shell, run:
ls squashfs-root/** | entr ./build-appimage.sh
The ** is a fish-ism, which is a lazy way of making it watch all files in the directory. There are other ways to achieve the same result. (It would probably work to use the same syntax with Bash if you set setopt globstar.) Or you can just use the path to the file you’re mainly editing:
ls squashfs-root/plugins/readeck.koplugin/main.lua | entr ./build-appimage.sh
Now, KOReader restarts with fresh code every time you hit save in your editor.
Check it out!
pay absolutely no attention to the spelling of "entries"