Shipping Tauri Android APKs That Play Nice With the Play Store
1. Problem Background
When building Android apps with Tauri, the locally packaged APK has a different signature from the version ultimately distributed on Google Play, causing:
- The local APK cannot directly overwrite the version installed from the Play Store
- APKs downloaded from other channels also cannot be updated normally by the Play Store
The root cause is the Play App Signing mechanism:
| Key Type | Holder | Purpose |
|---|---|---|
| Upload Key | Developer (local) | Signs the AAB uploaded to Play Console |
| App Signing Key | Hosted by Google | Signs the final APK downloaded by users |
A local APK signed with the Upload Key will inevitably have a different signature from the store version.
2. Correct Automation Goal
The goal is not to make the locally built Tauri APK have the same signature as the store version (nearly impossible, since you don't have the App Signing Key private key). Instead, it is:
- Sign the AAB with the Upload Key and upload it to Play Console
- Let Google generate the final APK using the App Signing Key
- Download this App Signing Key-signed Universal APK via the API
- Use this APK as the externally distributed version (official website, GitHub Release, other channels)
This way, after users install it, the Play Store can detect and update it normally.
3. Core Technical Solution
1. Signing Configuration (Tauri Side)
- Generate an Upload Keystore (
.jks) - Configure release signing in
src-tauri/gen/android/keystore.propertiesandbuild.gradle.kts - Build commands:
tauri android build # Generate AAB tauri android build --apk # Generate local test APK (signed with Upload Key)
2. Service Account Preparation (One-time)
- Create a service account in Google Cloud Console
- Enable the Google Play Android Developer API
- Download the JSON key file
- In Play Console → Users and permissions, invite the service account and grant "Release management" or "Test track release" permissions
- Store the JSON content in GitHub Secrets (
PLAY_SERVICE_ACCOUNT_JSON)
3. GitHub Actions Automation Flow
Recommended full pipeline:
Tauri builds AAB
↓
Upload AAB to Play Console (draft state)
↓
Wait for Google to generate the signed APK
↓
Call Generated APKs API to download Universal APK
↓
Upload to GitHub Release / own CDN / other channels
↓
(Optional) Promote the draft version to production release
Key APIs:
edits.bundles.upload: Upload AABgeneratedapks.list: Get list of downloadable APKsgeneratedapks.download: Download the signed Universal APK
Ready-made tools can also be used:
r0adkll/upload-google-play(upload AAB)- fastlane's
download_universal_apk_from_google_play(download signed APK)
4. Key Considerations
Signature difference is normal
The local APK (Upload Key) and the store version (App Signing Key) will always differ; this is by design.External distribution must use the Google-signed APK
Only the "Signed Universal APK" downloaded from Play Console / API can overwrite and be overwritten by the store version.Service Account permissions must be sufficient
At minimum, test track release permission is needed; downloading Generated APK recommends higher permissions.Handle latency
After uploading the AAB, Google typically needs a few minutes to generate the final APK; scripts should include appropriate waiting or polling.Security
The Service Account JSON must only exist in GitHub Secrets and must never be committed to the repository.
5. Final Recommended Practice
- Development/testing: Continue using the local Upload Key-signed APK; uninstall the store version before installing.
- External distribution / official website download: Always use the signed Universal APK downloaded from Play.
- Automated releases: GitHub Actions completes the full flow of "Build → Upload AAB → Download signed APK → Release".
With this approach, you can enjoy the security and optimization benefits of Play App Signing while achieving fully automated multi-channel distribution, ensuring a consistent user installation experience.
