Mobile App CI/CD Secrets: Android Keystore, Google Play and iOS Signing
Last updated: September 14, 2026
To sign mobile builds in GitHub Actions, add 11 required secrets: five for Android, including
ANDROID_KEYSTORE_BASE64 (your upload keystore encoded as base64), and six for iOS.
Add GOOGLE_PLAY_SERVICE_ACCOUNT_JSON to upload Android builds to Google Play automatically,
and three App Store Connect API secrets to upload iOS builds to TestFlight.
main and staging branches.
Other branches produce debug builds for testing only.
GitHub repo → Settings → Secrets and variables → Actions → New repository secret
Quick Reference: Every Mobile CI/CD Secret
Every secret the pipeline reads, what it holds, and how to create it. Select a secret name for the full steps.
| Secret | Needed for | What it is | How to create it |
|---|---|---|---|
ANDROID_PACKAGE_NAME |
Android, required | Your app's applicationId, for example com.example.app |
Copy applicationId from android/app/build.gradle |
ANDROID_KEYSTORE_BASE64 |
Android, required | Your upload (release) keystore file, encoded as base64 text | Create the keystore with keytool -genkeypair, then run base64 -i android-upload.keystore | pbcopy (macOS) or base64 -w 0 android-upload.keystore (Linux) |
ANDROID_KEYSTORE_PASSWORD |
Android, required | The keystore password | The password you entered when keytool created the keystore |
ANDROID_KEY_ALIAS |
Android, required | The name of the key entry inside the keystore | The -alias value you used; list it with keytool -list -keystore android-upload.keystore |
ANDROID_KEY_PASSWORD |
Android, required | The password for the key entry | For a PKCS12 keystore like the one above, the same value as the keystore password |
ANDROID_DEBUG_SHA256 |
Android, optional (deep links) | SHA-256 fingerprint of your debug signing certificate | keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android |
ANDROID_RELEASE_SHA256 |
Android, optional (deep links) | SHA-256 fingerprint of your upload certificate | keytool -list -v -keystore android-upload.keystore -alias yourapp |
ANDROID_PLAY_SIGNING_SHA256 |
Android, optional (deep links) | SHA-256 fingerprint of the key Google Play signs your app with | Play Console, your app, Play app signing, App signing key certificate |
GOOGLE_PLAY_SERVICE_ACCOUNT_JSON |
Android, optional (Google Play upload) | The JSON key of a Google Cloud service account allowed to release your app | Google Cloud Service Accounts, Keys, Add key, JSON; invite the account's email in Play Console Users and permissions; paste the whole file |
IOS_BUNDLE_ID |
iOS, required | Your app's Bundle ID, for example com.example.app |
Apple Developer Certificates, Identifiers & Profiles, Identifiers |
IOS_TEAM_ID |
iOS, required | Your 10-character Apple Developer Team ID | Apple Developer account, Membership |
APPLE_TEAM_ID |
iOS, required | The same Team ID, read by some workflow steps | Use the same value as IOS_TEAM_ID |
IOS_CERTIFICATE_BASE64 |
iOS, required | Your Apple Distribution certificate as a .p12 file, encoded as base64 |
Export it from Keychain Access on a Mac, then run base64 -i ios_distribution.p12 | pbcopy |
IOS_CERTIFICATE_PASSWORD |
iOS, required | The .p12 export password |
The password you set when exporting the certificate |
IOS_PROVISIONING_PROFILE_BASE64 |
iOS, required | An App Store provisioning profile, encoded as base64 | Download the .mobileprovision from Apple Developer Profiles, then run base64 -i YourProfile.mobileprovision | pbcopy |
APP_STORE_CONNECT_API_KEY_ID |
iOS, optional (TestFlight upload) | The Key ID of an App Store Connect API key | App Store Connect Users and Access, Integrations, App Store Connect API, Team Keys |
APP_STORE_CONNECT_API_ISSUER_ID |
iOS, optional (TestFlight upload) | The Issuer ID for your App Store Connect team | Shown on the same App Store Connect API keys page |
APP_STORE_CONNECT_API_KEY_BASE64 |
iOS, optional (TestFlight upload) | The API key's .p8 private key, encoded as base64 |
Download AuthKey_XXXXXXXXXX.p8 (you can download it only once), then run base64 -i AuthKey_XXXXXXXXXX.p8 | pbcopy |
base64 -i FILE | pbcopy copies an encoded file to the clipboard on macOS.
On Linux, run base64 -w 0 FILE and copy the single line it prints.
With the GitHub CLI, you can skip the clipboard:
base64 -i android-upload.keystore | gh secret set ANDROID_KEYSTORE_BASE64 on macOS, or
gh secret set GOOGLE_PLAY_SERVICE_ACCOUNT_JSON < service-account.json on either system.
Android Secrets
Configure these secrets to enable signed Android builds and optional Google Play uploads.
Required for Signed Builds
Your Android applicationId / package name used to identify your app.
com.example.app
How to get this value
- Open your Android project file:
android/app/build.gradleorandroid/app/build.gradle.kts
- Find the
applicationIdfield and copy its value - Add it to GitHub as
ANDROID_PACKAGE_NAME
Your Android upload keystore file encoded as base64.
How to get this value
Step 1: Create an upload keystore (do this once on a secure machine):
keytool -genkeypair -v \
-storetype PKCS12 \
-keystore android-upload.keystore \
-alias yourapp \
-keyalg RSA \
-keysize 2048 \
-validity 10000
keytool prompts for a keystore password and for name and organization details. Back up the keystore file and its password somewhere safe outside the repository.
Step 2: Convert the keystore to base64 (macOS):
base64 -i android-upload.keystore | pbcopy
On Linux, print it as a single line and copy the output:
base64 -w 0 android-upload.keystore
Step 3: Paste the copied output into GitHub secret ANDROID_KEYSTORE_BASE64
The password you set when creating the keystore.
How to get this value
Use the same password you typed during keystore creation in the previous step.
The alias inside your keystore that identifies the key entry.
yourapp (the -alias value you used)
The password for the key entry. For a PKCS12 keystore, such as one created with the command above, this is the same as the keystore password.
How to get this value
PKCS12 keystores don't support a separate key password, so keytool uses the keystore password for the key. Use that same value here. If you use an older JKS keystore with its own key password, enter that key password instead.
Optional (Deep Links & Auto-Upload)
Debug signing certificate SHA-256 fingerprint for deep links / assetlinks.
How to get this value
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey
Default password is android. Copy the SHA-256 fingerprint from the output.
Release signing certificate SHA-256 fingerprint for deep links / assetlinks.
How to get this value
keytool -list -v -keystore android-upload.keystore -alias yourapp
Copy the SHA-256 fingerprint from the output.
Google Play app signing certificate SHA-256 fingerprint.
How to get this value
- Open Google Play Console
- Select your app and open its Play app signing page (Google's help currently reaches it from Protected with Play)
- Copy the SHA-256 fingerprint for the App signing key certificate
A Google Cloud service account JSON key used by CI to upload the AAB to Google Play automatically.
How to get this value
Step 1: Create a service account key (Google Cloud)
- Open Service Accounts
- Select (or create) a Google Cloud project and enable the Google Play Android Developer API for it
- Create a service account (example:
github-actions-play-upload) - Open it → Keys → Add Key → Create new key → choose JSON
- Download the JSON file and store it securely
Step 2: Grant access in Google Play Console
- Open Google Play Console
- Go to Users and permissions → Invite new users
- Enter the service account's email address (it ends in
iam.gserviceaccount.com). You no longer need to link a Google Cloud project first. - Grant it permission to release your app, then send the invitation
Step 3: Add the secret to GitHub
- Open the downloaded JSON file
- Copy the entire JSON contents
- Paste into GitHub secret
GOOGLE_PLAY_SERVICE_ACCOUNT_JSON
iOS Secrets
Configure these secrets to enable signed iOS builds and optional TestFlight uploads.
Required for Signed Builds
Your iOS Bundle ID that uniquely identifies your app on the App Store.
com.example.app
How to get this value
- Open Apple Developer Identifiers
- Find your App ID and copy the Bundle ID
- Add it to GitHub secret
IOS_BUNDLE_ID
ANDROID_PACKAGE_NAME or com.example.app
Your Apple Developer Team ID (10 characters).
How to get this value
- Open Apple Developer Account
- Go to Membership and copy your Team ID
- Add it to GitHub secret
IOS_TEAM_ID
Same Team ID as above. Some workflows use both variables for compatibility.
IOS_TEAM_ID
Your Apple Distribution certificate exported as .p12 and base64-encoded.
How to get this value
- Create or confirm an Apple Distribution certificate at Apple Developer Certificates
- On a Mac, open Keychain Access and locate your Apple Distribution certificate under "My Certificates"
- Right-click → Export → save as
ios_distribution.p12and set a password - Convert to base64:
base64 -i ios_distribution.p12 | pbcopy
Paste into GitHub secret IOS_CERTIFICATE_BASE64
The password you set when exporting the .p12 certificate.
How to get this value
Use the export password you chose in Keychain Access when exporting the certificate.
An App Store provisioning profile (.mobileprovision) encoded as base64.
How to get this value
- Open Apple Developer Profiles
- Create an App Store provisioning profile for your Bundle ID
- Download the
.mobileprovisionfile - Convert to base64:
base64 -i YourProfile.mobileprovision | pbcopy
Paste into GitHub secret IOS_PROVISIONING_PROFILE_BASE64
Optional (TestFlight Upload)
Key ID for an App Store Connect API key.
How to get this value
- Open App Store Connect Users and Access
- Go to Integrations → App Store Connect API → Team Keys and generate a key with the App Manager role (only the Account Holder or an Admin can generate keys)
- Copy the Key ID and save it as
APP_STORE_CONNECT_API_KEY_ID
Issuer ID shown on the App Store Connect Keys page.
How to get this value
- Open App Store Connect Users and Access
- Go to Integrations → App Store Connect API and copy the Issuer ID shown on that page
- Save it as
APP_STORE_CONNECT_API_ISSUER_ID
The downloaded .p8 private key file encoded as base64.
How to get this value
- When you create the App Store Connect API key, download the
AuthKey_XXXXXXXXXX.p8file (one-time download) - Convert to base64:
base64 -i AuthKey_XXXXXXXXXX.p8 | pbcopy
Paste into GitHub secret APP_STORE_CONNECT_API_KEY_BASE64
Prerequisites & Security
Developer Accounts Required
- Google Play Console — Required for Android app distribution
- Apple Developer Program — $99/year membership required
- App Store Connect — For TestFlight and App Store distribution
If you enroll as an organization/business, Apple (and often Google) will require business verification, typically including a D-U-N-S number.
Security Recommendations
- Use GitHub Environments for
stagingandproductionwith environment-protected secrets - Limit who can run workflows on
mainandstagingbranches - Never commit keystores, certificates, provisioning profiles, or API keys to your repo
- Rotate secrets periodically and after team member departures
Mobile CI/CD Secrets FAQ
What is ANDROID_KEYSTORE_BASE64?
ANDROID_KEYSTORE_BASE64 is your Android upload keystore stored as base64 text. GitHub secrets hold text, not files, so the workflow decodes the value back into a keystore file and uses it with ANDROID_KEY_ALIAS and the two passwords to sign release builds.
How do I base64-encode a keystore on macOS or Linux?
On macOS, run base64 -i android-upload.keystore | pbcopy to copy the encoded keystore to your clipboard. On Linux, run base64 -w 0 android-upload.keystore and copy the single line it prints. Paste the result into a repository secret named ANDROID_KEYSTORE_BASE64.
Is the upload keystore the same as the release keystore?
With Play App Signing, the keystore you create here holds your upload key. You sign each release with it, and Google Play signs the APKs delivered to users with an app signing key that Google manages. If you lose the upload key, you can request an upload key reset in Play Console, but back up the keystore and its passwords anyway.
What is GOOGLE_PLAY_SERVICE_ACCOUNT_JSON, and do I need it?
It is the JSON key of a Google Cloud service account that you have invited to your Google Play Console account. The pipeline uses it to upload your Android App Bundle to Google Play. It is optional: the required secrets are enough for signed builds, and this one only adds the automatic upload.
Why does my workflow produce a debug build instead of a signed one?
Signed builds and store uploads run only on the main and staging branches; other branches produce debug builds for testing. If you are on one of those branches, confirm each required secret exists under Settings, Secrets and variables, Actions, spelled exactly as in the quick-reference table.
Is it safe to store signing keys in GitHub Actions secrets?
GitHub encrypts Actions secrets and redacts their values from workflow logs. Reduce the risk further: use GitHub Environments with protection rules for staging and production, limit who can run workflows on those branches, never commit keystores or certificates to the repository, and rotate secrets when people leave the team.