I'm making Kosti 3D, a survivor-like set in Slavic folklore: top-down 3D, auto-aim, a hero fighting his way through four chapters to Koschei the Deathless. It's built in Godot 4.6 and aimed at Android phones. My test floor is a Redmi 9. It isn't on Play yet; I'm getting it there.
Writing the game was the comfortable part. Getting it into a shape Google Play accepts is where I lost evenings. None of the problems were hard; they were boring, easy to forget, and showed up as vague export errors or a build that was far too big.
After hitting the same issues a few times, I turned the checks into a small CLI called godot-ship-kit (gsk). The problems come first here, because they're useful even if you never install anything.
1. SDK, JDK and export templates
Godot's Android export needs three things in the right place: the Android SDK (with platform-tools, build-tools and cmdline-tools), JDK 17, and export templates for exactly your Godot version. When I moved the project to a new build server, every error came down to wrong paths or templates for a different patch version.
A worse case: a cleanup script deleted the project's android/build/ folder, which is the Gradle build template. The next export failed with "build template not installed". It took a while to connect the two.
gsk doctor checks all of this up front: the Godot binary against the project's version, templates for that exact version (including .NET builds), each SDK component, the JDK version, and whether the Gradle template matches Godot. Every finding comes with a fix: line. gsk configure writes the SDK and JDK paths into Godot's editor settings for you.
2. The keystore is the one file you can't lose
Once a game is on Play, every update must be signed with the same upload key. I keep mine outside the project with an offline backup, and I never want its password sitting in export_presets.cfg.
doctor looks for a release keystore from environment variables, the preset or export credentials, checks that the password and alias are present (it never prints them), and fails if a keystore, export_credentials.cfg or a plain-text password ends up tracked by git. gsk build takes signing from the standard Godot environment variables, so CI can use secrets:
export GODOT_ANDROID_KEYSTORE_RELEASE_PATH=/secure/upload.keystore
export GODOT_ANDROID_KEYSTORE_RELEASE_USER=upload
export GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD=... # from your secret store
gsk build
A release build stops before exporting if no keystore is configured.
3. AAB, target API and 16 KB pages
I tested Kosti as an APK for months because it's easy to sideload. But new apps on Play need an AAB, which means the Gradle build, which means the build template from point 1.
Then there are the numbers Google keeps moving. The target API level requirement is now 36 (since 31 Aug 2026), and 64-bit native libraries need to support 16 KB memory pages. The editor won't tell you.
gsk build exports headless and then verifies the result: zip integrity, signature (apksigner) and zipalign (when build-tools are installed), the manifest (package, version, target SDK, permissions), and 16 KB alignment, read straight from the ELF headers of each 64-bit .so. You can also run that part alone on any APK or AAB with gsk verify. One detail I learned the hard way: Godot's exit code isn't a reliable signal after a failed export, so gsk build treats a missing or stale output file as a failure and keeps the log.
All Play numbers live in one file, each with its source and the date it was checked.
4. A 651 MB APK
At one point the Kosti APK was 651 MB. My first thought was the 3D models. It wasn't, mostly.
The size of your PNG or JPEG doesn't matter much. What ships is the copy Godot makes in .godot/imported, and that size comes from the .import settings. A texture imported Lossless at full resolution can be much bigger than its source (especially a JPEG), and far bigger in GPU memory. AI-generated 3D models made this worse for me: they come with large textures and import with heavy defaults.
Re-importing about 400 textures with VRAM compression (ETC2) and size caps, plus removing duplicate assets, brought the APK from 651 MB to 273 MB. The caps do most of the download savings; ETC2 is mainly a GPU-memory win and can even be bigger on disk. I did that by hand the first time. gsk slim is the automated version:
- it measures every imported file and maps it back to its source,
- proposes a safe tier (VRAM compression for textures used in 3D, normal-map compression) and, with
--aggressive, a review tier (size caps, lossy WebP for big 2D art, QOA for long WAVs), -
--applybacks up every.importit touches,--undorestores them, and--reimportruns Godot and prints the measured result.
It also lists unreferenced assets and heavy GLB models. Estimates assume ETC2, so trust the --reimport numbers.
5. The menu that was cut off on 20:9 phones
My menus were designed at 9:16 and looked fine in the editor. On a modern 20:9 phone, the menu sat between black bars and the bottom button's label was cut off.
I already had a rule, after sending test builds with visual changes I hadn't looked at: render the game headless on the server and look before building. I use AI coding agents a lot, and an agent can't see a phone. So that habit became gsk eyes.
gsk eyes starts Godot under a virtual display with software OpenGL (Mesa llvmpipe), so it needs no GPU. It loads each scene at real phone resolutions, can tap buttons or call methods first, and saves a screenshot. It uses the renderer your project uses on mobile, not the desktop one. Then it compares each capture against a committed reference image and reports:
- similarity, changed pixels and the largest changed region (so one wrong digit in a price still fails),
- any Control that sticks out past the screen edge, with the overflow in pixels,
- black frames, meshes with no material, a 3D scene with no camera, a rigged model whose animations aren't playing, and script errors.
You get an HTML report with a before/after slider, plus report.md and report.json for agents with full-resolution crops of changed regions. The crops matter: a 1080x2400 screenshot gets downscaled until small text is unreadable to an agent; the crop is where it notices the clipped label.
Software rendering is deterministic, which is what makes reference images work in CI. The repo's CI runs the capture on Godot 4.2, 4.4, 4.5 and 4.6.
Trying it
pipx install "godot-ship-kit[eyes] @ git+https://github.com/vladosskins178/godot-ship-kit"
export GODOT=/path/to/godot # if godot isn't on PATH
cd my-game
gsk doctor # what blocks an Android release?
gsk slim # what makes the build big?
gsk eyes --scene res://ui/menu.tscn # what does it look like on a phone?
gsk build --debug # export and verify
gsk play-checklist -o PLAY_CHECKLIST.md # Play Console launch checklist
doctor, slim, build and play-checklist use only the Python standard library (Python 3.11+). eyes adds Pillow and numpy, and on headless Linux needs xvfb and Mesa.
The repo is also a GitHub Action (run eyes --strict or doctor --strict on every push), there's an MCP server (gsk mcp) whose gsk_eyes returns the crops as images, and an agent skill (gsk skill print >> AGENTS.md for Codex, Cursor and others).
What it doesn't do
- It doesn't show performance. Software rendering shows layout, colour, composition and missing assets. Frame rate and GPU-specific bugs still need a real phone. I still test on the Redmi.
- It isn't an editor bridge. Tools like godot-mcp drive the editor while you build the game. gsk is for the part after that, and the two work fine side by side.
-
Animated scenes and Godot upgrades need care. Moving scenes need a fixed
waitand a looser threshold; references must be regenerated when you upgrade Godot (gsk records the version and warns). -
eyes,buildandslim --reimportrun your project's scripts, same as opening it in the editor. Use them on projects you trust. - It's version 0.1, developed mainly on Linux with Godot 4.6.3.
doctor,slim,buildandverifyalso run on macOS and Windows.
Feedback welcome
The code is MIT-licensed: https://github.com/vladosskins178/godot-ship-kit
If you've shipped a Godot game to Play, tell me which check you wish you'd had, or which one gives a wrong answer on your project. Issues and PRs are welcome; a bug report with a small repro project is the most useful thing you can send.
Written with the help of an AI assistant; the tool and the experiences are mine.

Top comments (0)