Ship to Google Play
Build an Android App Bundle on EAS, create the app in Play Console, set up a service account, then submit to internal testing with one command.
Hey - by the end of this page your app is on the Play internal testing track, installable by
the testers you invite, and every build goes up with one eas submit.
Before you start
- Tier: Starter or Pro. The Free tier ships without
eas.json- runbunx eas-cli build:configureonce to create one, and skip thebun run doctorchecks. - Time: 1-2 hours of your own work, plus 20-40 minutes per cloud build. A new Play developer account has to pass Google's identity verification first, which can take a few days.
- Accounts: a Google Play developer account (a one-time fee), a free Expo account, and a Google Cloud project for the service account (step 5).
- Previous tutorial: Ship to TestFlight. If you skipped it, do
its steps 1-3 first: branding,
bunx eas-cli initwith the id pasted intoapp.easProjectIdinreadynative.config.ts, and your keys on EAS.
1. Set the package name for good
app.android.package in readynative.config.ts is your Play application id, for example
com.yourcompany.trailmix. The production profile uses it as is (dev and preview append
.dev / .preview). Once the first bundle is uploaded, Play locks the app to this id, so
change it now or never. bun run doctor warns while it's still com.acme.app.
bun run doctorYou should see: no warning for app.android.package.
2. Check your keys are on EAS
.env is gitignored and never uploaded, so the build sees only the keys stored in its EAS
environment - production for this profile. If you set them up for iOS, they're already there:
Android uses the same environment. Check:
bunx eas-cli env:list --environment productionYou should see: every key from your .env, with production values. Missing one? Add it with
bunx eas-cli env:set --name <KEY> --value <value> --environment production --visibility plaintext (sensitive for keys without the EXPO_PUBLIC_ prefix) - see
Environment variables → On EAS.
3. Build the production bundle
bunx eas-cli build --profile production --platform androidThe production profile in eas.json sets APP_VARIANT=prod, builds an Android App Bundle
(.aab) and auto-increments the version code. On the first Android build, EAS offers to
generate an upload keystore; say yes. EAS keeps it, and bunx eas-cli credentials shows it and
its SHA-256 fingerprint.
Using App Links? links.androidCertFingerprints needs the fingerprint of the key that signs what
users install. New apps always use Play App Signing, so Google signs the installed app with its
own app signing key: copy that key's SHA-256 from Play Console → Protected with Play → Play
Store distribution → Play app signing and add it next to the upload key's.
You should see: the build marked Finished on expo.dev, with an .aab artifact.
4. Create the app in Play Console
In Play Console → Create app, pick the app name, the default language, App vs Game and Free vs Paid, and accept the declarations. Play doesn't ask for the package name here - it takes it from the first bundle that reaches Play.
You should see: the app's dashboard in Play Console, with a "Set up your app" task list.
5. Create a service account for eas submit
EAS submits with a Google service account key. Expo's service account guide has screenshots of every click; the short version:
- In Google Cloud Console, pick (or create) a project and create a service account. Copy its email, then Manage keys → Create new key → JSON and download the file.
- Enable the Google Play Android Developer API for that project.
- In Play Console → Users and permissions → Invite new users, invite the service account's email. On App permissions, pick your app and tick: view app information, edit and delete draft apps, release to production (with Play App Signing), release to testing tracks, manage testing tracks and tester lists, and manage store presence. It doesn't need admin or financial permissions.
Then either upload the key to EAS (expo.dev → your project → Credentials → Android → your
application id → Add a Google Service Account Key, or bunx eas-cli credentials --platform android → Google Service Account), or keep it next to the project and point eas.json at
it:
"submit": {
"production": {
"android": {
"serviceAccountKeyPath": "./google-service-account.json",
"track": "internal",
"releaseStatus": "draft"
}
}
}The key can publish your app, so add google-service-account.json to .gitignore before you
commit anything. With the key uploaded to EAS instead, leave serviceAccountKeyPath out.
track is internal (the default), alpha (closed testing), beta (open testing) or
production. releaseStatus is completed (the default: rolled out), draft, inProgress
(a staged rollout, with rollout between 0 and 1) or halted. While the app has never been
published - Play Console still calls it a draft app - Google's API only accepts draft
releases and rejects anything else with "Only releases with status draft may be created on
draft app". Keep releaseStatus: "draft" until the app has gone through review, then drop it,
or every later release also waits in draft for you to roll it out by hand.
You should see: the service account listed under Users and permissions, and the key
under your project's Credentials on expo.dev (or the JSON file next to eas.json, ignored by
git).
6. Upload the first bundle (optional)
eas submit can create the app's first release itself, so you can go straight to step 7. To
do the first upload in the browser instead
(Expo's manual guide walks through it):
- Test and release → Testing → Internal testing → Create new release.
- Accept Play App Signing when asked (Google holds the app signing key; your EAS keystore becomes the upload key).
- Upload the
.aabfrom step 3 (download it from the build page on expo.dev), add release notes and roll it out.
Either way, under Internal testing → Testers, create an email list, add yourself, and open the opt-in link on your phone. After that the build installs from the Play Store app on every tester's device.
You should see: the opt-in page on your phone saying you're a tester.
7. Submit
bunx eas-cli build --profile production --platform android --auto-submitOr submit a build you already have:
bunx eas-cli submit --profile production --platform androidThe release lands on the internal testing track. With releaseStatus: "draft", open it in
Play Console (Internal testing → Releases) and roll it out; otherwise testers get it within
minutes. For later releases and wiring this into your own CI, see
Ship to TestFlight → Automate it.
You should see: the release under Internal testing → Releases, and after rollout, the app installable from the Play Store app on your phone.
New personal accounts need a closed test first
Personal developer accounts created after November 13, 2023 can't publish to production until a
closed test has run with at least 12 testers opted in continuously for at least 14 days; then you
apply for production access on the Play Console dashboard. Internal testing doesn't count. Start
the closed test early (track: "alpha" submits to it). The rule is for personal accounts;
organisation accounts don't have it. Google's testing requirements
page has the details.
Check it
bun run doctorEvery EAS row should be green. Before production, bun run doctor --store (Pro) checks what Play
review looks at.
If it doesn't work
- "Only releases with status draft may be created on draft app" - set
"releaseStatus": "draft"insubmit.production.androiduntil the app has passed its first review. eas submitsays the service account has no permission - the invite in Play Console is missing an app permission from step 5, or the Google Play Android Developer API isn't enabled for its Cloud project. Permission changes can take a few minutes to apply.- "Package name … already exists" - another app on Play uses that id. Change
app.android.packageand build again. - The app works locally but shows "Configure …" from Play - a key is missing from the EAS
productionenvironment. See A key works locally but not in an EAS build. - The build fails on the keystore or credentials - see EAS build or credentials fail.
More in Troubleshooting.
Congrats 🎉
Your app is on Google Play internal testing, and the next build goes up with one command. Before you move to production, fill in the store listing, content rating and Data safety form in Play Console - Pass app review covers what reviewers check.
Next, ship a fix without a rebuild: Your first update.