
Your own Android app, outside the Play Store, with its own updates
Our console app for the agent network runs on our phones without ever going through an app store: we build it, sign it and serve its updates ourselves. It is simpler than it sounds, as long as you respect two things: the signing key and the version number.
1. Build in a container
You do not need Android Studio on your machine. A container image with the Android SDK builds the project from the command line, and a cache folder for Gradle keeps rebuilds fast:
docker run --rm -v "$PWD":/app -v ~/.cache/gradle:/root/.gradle -w /app \
ghcr.io/cirruslabs/android-sdk:35 ./gradlew assembleRelease
2. Sign with a release key
A debug-signed APK installs, but Play Protect warns or blocks it, and its key differs on each machine. Create a release keystore once, sign every build with it, and back it up like a password: an update signed with another key cannot replace the installed app, and your users would have to uninstall it and lose its data.
3. Your own update channel
Next to the APK, publish a small file on your server, for example latest.json, with the version number, the version name, the download address and the file's checksum. At start, the app reads it, compares the number with its own, and offers the update. Serve both over your private network (Homelab 6): your app is not public, its updates do not need to be.
On the phone, allow the app to "install unknown apps" once, so it can install its own updates.
The rule that bites
The app compares the versionCode, an integer, nothing else. Publish two different builds with the same number and the second one is never offered: phones believe they are up to date. So a version number is spent the moment it is published, and you never raise it ahead of a release, because a number merged but not published is spent too. Raise it in the change that will be released, build and publish right after the merge.
Traps
Test the update path, not only the install: install the previous version, publish the new one, and check that the phone offers it and installs it over the old one with its data intact.
Keep the keystore and its passwords out of the repository: in your vault for the passwords (Homelab 3), and the file in your backups (Homelab 5).
Found this useful? Tell us what to measure next.