Deploying Unity Builds to Google Play Store

Deploying Unity Builds to Google Play Store
Getting your game onto the Google Play Store involves a complex dance of signing keys, version codes, and console configurations. Manually building an Android App Bundle (AAB), signing it, and uploading it to the Play Console every time you want to test a change is a recipe for burnout.
In this guide, we’ll walk through how to automate your Unity builds for Android and deploy them directly to the Google Play Store internal testing track using GitHub Actions.
Table of Contents
- Prerequisites
- Setup Overview
- Setting up Google Play Console
- Configuring Google Cloud Access
- Handling the Keystore
- Updating the Build Pipeline
- Deploying to Internal Testing
Prerequisites
Before we start, make sure you have:
- Google Play Developer Account: You need to be able to access the Google Play Console.
- Working Unity Build: You should have a basic automated build pipeline set up. Check out our GitHub Actions Unity guide if you’re starting from scratch.
Setup Overview
Automating Android deployments involves two key security components: Android Keystores (for Android Signing) and Service Accounts.
- App Registration: Create the app placeholder in Google Play Console.
- Authentication: We’ll use Workload Identity Federation to securely authenticate GitHub Actions Android workflows with Google Cloud.
- Keystore: We need to securely provide our signing keystore to the build server.
- Build & Upload: We’ll update the workflow to build an Android App Bundle (
.aab) and upload it.
Setting up Google Play Console
First, let’s make sure Google is expecting our app.
- Log in to Google Play Console.
- Click Create app.
- Fill in the app details (Name, Default language, Game/App type).
- Important: Manually build your APK/AAB in Unity and upload it to the Internal Testing track via the web interface. This initializes the track, sets the Package Name permanently, and allows you to answer the initial regulatory questions (Data Safety, etc.).
Configuring Google Cloud Access
To allow GitHub Actions to upload via the API, we need to link our Google Play account to a Google Cloud project. We will use a modern, secure method called Workload Identity Federation.
1. Enable the Google Play Android Developer API
- Go to the Google Cloud Console.
- Select or create a project for your game.
- Search for Google Play Android Developer API and enable it.
2. Create a Service Account
- In Google Cloud Console, go to IAM & Admin > Service Accounts.
- Click Create Service Account.
- Name it something like
unity-cicd-uploaderand click Create and Continue. - Skip the role assignment steps (click Continue then Done). This service account does not need specific Google Cloud permissions, only Google Play permissions.
- Copy the email address (e.g.,
unity-cicd-uploader@my-project.iam.gserviceaccount.com). - Go back to Google Play Console > Users and permissions and invite this email.
- In the permissions tab, grant it App Access to your game, and under Account permissions, ensure it has Releases to play store > Release to testing tracks (or Admin if you want full control).
3. Setup Workload Identity Federation
This step is a bit “in the weeds” of cloud security, but it’s worth it to avoid handling sensitive JSON key files.
- In Google Cloud Console, go to IAM & Admin > Workload Identity Federation.
- Create a Pool (e.g.,
unity-cicd-pool). - Create a Provider inside that pool:
- Type: OpenID Connect (OIDC)
- Issuer (URL):
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com(Standard default for GitHub Actions).
- Connect your Service Account to this pool. You’ll need to allow the specific GitHub repository to impersonate this service account.
If strict security configuration sounds daunting, you can generate a JSON key for your Service Account and use that, but Workload Identity is the recommended best practice.Tip
Handling the Keystore
Your Android Keystore is the identity of your app. If you lose it, you can’t update your app. If it’s stolen, someone else can update your app.
For CI/CD, we have two common approaches:
- Base64 Encode: Encode the
.keystorefile to a base64 string and store it as a GitHub Secret. - Repo Storage: Commit the keystore file to your repository (if private) or encrypted.
Method 1: Base64 Encoding (Recommended)
This method keeps your keystore out of your repository entirely.
Encode: Run one of the following commands to turn your file into a text string.
macOS / Linux:
openssl base64 -in user.keystore -out user.keystore.base64.txtWindows (PowerShell):
[Convert]::ToBase64String([IO.File]::ReadAllBytes("user.keystore")) | Set-Content user.keystore.base64.txtStore: Copy the contents of the text file and add it as a secret named
ANDROID_KEYSTORE_BASE64.
Method 2: Repo Storage (Not Recommended)
You can commit the keystore file to your repository (e.g. in Assets/Keys/user.keystore), but this is risky. If your repo is ever compromised or made public, your signing key is exposed. If you choose this route, ensure your .gitignore does not ignore .keystore files.
Regardless of the method, go to your GitHub Repository > Settings > Secrets and variables > Actions and add:
ANDROID_KEYSTORE_PASSWORD: The password for your keystore.
Updating the Build Pipeline
Now let’s update our unity-build.yml workflow to include the Unity Android Build steps.
1. Decode the Keystore
If you used the recommended Base64 method, we need to decode the secret back into a file before the build starts. Add this step before the build action:
- name: Decode Keystore
if: ${{ matrix.build-target == 'Android' }}
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > ${{ github.workspace }}/user.keystore 2. Build Arguments
We need to tell Unity where to find the keystore and what password to use. Note that if you decoded the keystore in the previous step, it will be at ${{ github.workspace }}/user.keystore.
# Android build arguments
if [[ "$buildTarget" == "Android" ]]; then
buildArgs+=" -keystorePath ${{ github.workspace }}/user.keystore"
buildArgs+=" -keystorePass ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}"
buildArgs+=" -keyaliasName "my-key-alias""
buildArgs+=" -keyaliasPass ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}"
buildArgs+=" -appBundle -minifyRelease"
fi 3. Add Authentication Step
We use the google-github-actions/auth action to log in using the Service Account we created.
- uses: google-github-actions/auth@v3
if: ${{ matrix.build-target == 'Android' }}
with:
service_account: unity-cicd-uploader@my-project.iam.gserviceaccount.com
workload_identity_provider: projects/123456789/locations/global/workloadIdentityPools/unity-cicd-pool/providers/unity-cicd-provider 4. Add Upload Step
We’ll use the RageAgainstThePixel/upload-google-play-console action. This handy community action handles the actual upload logic.
We also need to make sure Java is installed, as the uploader tool requires it.
- uses: actions/setup-java@v5
if: ${{ matrix.build-target == 'Android' }}
with:
distribution: temurin
java-version: 21
- uses: RageAgainstThePixel/upload-google-play-console@v1
if: ${{ matrix.build-target == 'Android' }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
release-directory: ${{ env.UNITY_PROJECT_PATH }}/Builds/Android
track: internal
status: draft
metadata: |
{
"releaseNotes": {
"language": "en-US",
"text": "${{ env.RELEASE_NOTES }}"
}
} We are deploying to theNote
internaltrack. This is usually the best place for automated builds so your team can test them immediately.
Deploying to Internal Testing
Once you push these changes, your workflow should:
- Build the Android App Bundle (
.aab). - Authenticate with Google Cloud.
- Upload the
.aabto the Internal Testing track on the Play Console.
When the upload finishes, your testers in the Internal Testing email list will receive an update (or see it in the Play Store if they have it installed).
Common Gotchas
- Android Version Code: Google Play requires a unique integer
versionCodefor every single upload. Ensure your build script increments this on every build (e.g., using${{ github.run_number }}) in your Unity Android Build settings or build script. - Android Build Number: Similar to
versionCode, the user-visiblebundleVersion(or Unity App Version) should be updated to help testers identify which build they are on. - Play App Signing: When you create your app in the console, you likely opted into Play App Signing. This means Google manages the key used to sign APKs delivered to users. The keystore we configured above is your Upload Key. If you lose it, you can contact Google support to reset it, whereas the final signing key is safely stored by Google.
- API Delays: Sometimes the Google Play API takes a few minutes to process a new build.
- Permissions: If you get a 403 error, double-check that the Service Account email is added to the Google Play Console Users with the correct permissions and linked to the Google Cloud project.
Automating this pipeline saves you from the “Build -> Wait -> Upload -> Wait” loop, letting you focus on making your game fun.
Further Reading
- Automating Unity Builds to iOS, macOS, and visionOS
- How to Stream Unity Logs from Your Game
- How to Download and View Unity Logs from Your Game
Subscribe to our Newsletter
Get the latest news and updates from Virtual Maker delivered straight to your inbox.
© 2026 Virtual Maker Corporation





