Some apps have no business being searchable. A tool built for one company, a client demo, a closed beta you want a dozen people to poke at — none of these belong in public Play search. Google still lets you ship them through Play; the listing just stays invisible to everyone who wasn't invited.
Here's the loop I follow: create the app record, finish every declaration, push an AAB to the internal testing track, hand testers an opt-in link, and keep adb nearby as a fallback for local checks. It takes maybe 30 minutes of clicking the first time and five minutes every release after that.
What "private" actually means on Google Play
An app that lives on a testing track does not appear in Play Store search. The only way in is an opt-in URL that Google generates for you, and the Store verifies that the account signed in on the phone is on your tester list before it even renders a Download button.
Two consequences follow from that:
- You cannot "just send the APK" to someone outside the list and expect Play to serve them.
- Your store card still gets rendered inside the Play app, so icons, screenshots and descriptions are mandatory even though nobody will browse to them.
This works for any app targeting Android 8.0 (API 26) or newer, built in Android Studio and uploaded as an AAB — one bundle file that Google splits into per-device APKs. Internal testing caps out at 100 testers per app, which is plenty for QA and early feedback.
What you need before opening the Console
- A Google account enrolled in the Play Console developer program, with identity and payment profile verified. One-time registration fee, US$25 when I last paid it.
- A release build signed with your own upload key. Pick a package name you can live with forever, like
com.example.privateapp. - Play App Signing enabled. Google holds the real signing key; you keep only the upload key. This is the default for new apps, and you should not fight it.
- A Console role of Account owner, Admin, or Release manager. A Release manager can upload bundles but can't touch payment settings.
- Gmail addresses for your testers — exactly the accounts they'll be signed into on their phones.
- A privacy policy URL if the app touches any user data. The Data safety form blocks the release without it.
I run a short pre-flight before touching anything, because almost every stalled rollout traces back to one of these being wrong:
- [ ]
applicationIdmatches the package name you intend to keep. - [ ]
versionCodehas never been uploaded before — start at 1. - [ ]
targetSdkis at least what the Console currently demands. - [ ] 512×512 PNG icon, 1024×500 feature graphic, two phone screenshots.
- [ ] Short description (80 chars) and full description (4000 chars) written.
- [ ] One tester address you personally control, for a smoke test before you annoy the team.
A minimal starting point looks like this: applicationId "com.example.privateapp", versionCode 1, versionName "1.0.0".
The release flow, step by step
- Create the app record. In Play Console go to All apps > Create app, fill in name, default language, App or Game, Free or Paid, tick the declarations, and hit Create app. You land on a dashboard with a Set up your app checklist. No app record means no place to attach a bundle, so this comes first.
- Finish App content. Open Policy and programs > App content and clear every card: privacy policy, Data safety, ads, content rating questionnaire, target audience, news app. You want a green check on each row. A release will sit at "Pending" forever while any of these is incomplete — this is the number one cause of a rollout that looks stuck.
- Fill in the store listing. Under Grow > Store presence > Main store listing, add name, both descriptions, icon, feature graphic and screenshots, then save. The page should save without red "Missing" badges.
- Create the internal release. Go to Test and release > Testing > Internal testing (older Console layouts put it under Release > Testing > Internal testing), click Create new release, accept Play App Signing, upload the
.aab, name the release something like1.0 internal, add notes. Then Save → Review release → Start rollout to Internal testing. Within a few minutes the status flips to Available to internal testers, because the internal track skips the full review queue. - Build the tester list. On the same page open the Testers tab, create an email list (I call mine
qa-team), paste the Gmail addresses, save, then copy the Join on Android / Join on the web opt-in link. It looks likehttps://play.google.com/apps/internaltest/1234567890123456789. That link is the only entrance — nothing is discoverable in search. - Send the link. Each tester opens it while signed into the exact account from your list, taps Become a tester, then Download it on Google Play. The result is an ordinary Play install, and the app shows up under My apps.
Installing without the Play Store
Sometimes you can't wait for propagation, or you want to test the release build on your own desk before anyone else sees it. Build the APK and push it over USB:
adb devices # example output: 1d2f3a4b5c6d device adb install -r app/build/outputs/apk/release/app-release.apk # example output: Success adb shell pm list packages com.example # example output: package:com.example.privateapp adb bypasses the Store completely, which is exactly what you want while a release is still in review or while you're chasing a bug that only reproduces on one physical device.
Three failures I've hit, and what fixed them
The opt-in link returns "requested URL was not found." The Console says the release is available, the browser disagrees. Check in this order: the tester is signed into the exact Gmail address on the list (a different account always fails); the release status really is Available to internal testers and not Draft or Halted; a few minutes have passed since rollout; and you copied the link from the Testers tab of the track you actually rolled out to.
Upload fails with a signing error after reaching 100%. The Console only accepts bundles signed with your registered upload key, so a debug-signed build is rejected on purpose. Rebuild via Build > Generate Signed Bundle / APK > Android App Bundle and point it at your .jks. If the upload key is genuinely gone, use Reset upload key in the Console and upload the new certificate.
"Your app currently targets API level X and must target at least API level Y." Google raises the floor every year, and an old bundle can't be reused — the version code has to go up. Bump both values and rebuild:
android { defaultConfig { applicationId "com.example.privateapp" versionCode 2 targetSdk 34 } } Staying private or widening the circle
When testers stop complaining, you can promote the same bundle onward. Closed testing gives you larger lists and Google Groups instead of a hand-typed email list, and multiple tracks (alpha/beta), though the release may go through review first. Production is the obvious next step if the app is meant to be public.
If the app must stay inside one organization, link your company to Play Console and publish it through Managed Google Play — it then shows up only in that organization's private store. That's the right answer for internal tools, and it's a different setting from the tester link, so don't mix them up.
On the FAQ side, two questions come up constantly. Internal vs closed testing: internal is the fast path — up to 100 testers, an email list, live in minutes; closed is for scale and review-gated rollouts. Will it show up in search? Not while it's on a testing track, and not if you publish through Managed Google Play.
Takeaway
Private distribution on Play is not a special build or a secret switch — it's an ordinary AAB on a track that isn't indexed, plus an opt-in link gated by an email list. Get the App content declarations done early, keep your version code monotonic, and let internal testing be your default target until the app earns a wider audience.
This article was originally published on AI Release — https://ai-release.org. If you want more walkthroughs like this one, the channel is at https://t.me/ai_release1.
