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.

Template Availability: Mobile app CI/CD is currently only available for the Express.js (Bedrock Express) template. Python/Flask templates do not include mobile build capabilities at this time.
Important: Signed builds and marketplace uploads run only on the main and staging branches. Other branches produce debug builds for testing only.
Where to add secrets:
GitHub repo → SettingsSecrets and variablesActionsNew 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
macOS and Linux commands: 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

ANDROID_PACKAGE_NAME Required

Your Android applicationId / package name used to identify your app.

Example: com.example.app
How to get this value
  1. Open your Android project file:
    • android/app/build.gradle or android/app/build.gradle.kts
  2. Find the applicationId field and copy its value
  3. Add it to GitHub as ANDROID_PACKAGE_NAME
ANDROID_KEYSTORE_BASE64 Required

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

ANDROID_KEYSTORE_PASSWORD Required

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.

ANDROID_KEY_ALIAS Required

The alias inside your keystore that identifies the key entry.

Example: yourapp (the -alias value you used)
ANDROID_KEY_PASSWORD Required

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)

ANDROID_DEBUG_SHA256 Optional

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.

ANDROID_RELEASE_SHA256 Optional

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.

ANDROID_PLAY_SIGNING_SHA256 Optional

Google Play app signing certificate SHA-256 fingerprint.

How to get this value
  1. Open Google Play Console
  2. Select your app and open its Play app signing page (Google's help currently reaches it from Protected with Play)
  3. Copy the SHA-256 fingerprint for the App signing key certificate
GOOGLE_PLAY_SERVICE_ACCOUNT_JSON Optional Enables Auto-Upload

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)

  1. Open Service Accounts
  2. Select (or create) a Google Cloud project and enable the Google Play Android Developer API for it
  3. Create a service account (example: github-actions-play-upload)
  4. Open it → KeysAdd KeyCreate new key → choose JSON
  5. Download the JSON file and store it securely

Step 2: Grant access in Google Play Console

  1. Open Google Play Console
  2. Go to Users and permissionsInvite new users
  3. Enter the service account's email address (it ends in iam.gserviceaccount.com). You no longer need to link a Google Cloud project first.
  4. Grant it permission to release your app, then send the invitation

Step 3: Add the secret to GitHub

  1. Open the downloaded JSON file
  2. Copy the entire JSON contents
  3. 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

IOS_BUNDLE_ID Required

Your iOS Bundle ID that uniquely identifies your app on the App Store.

Example: com.example.app
How to get this value
  1. Open Apple Developer Identifiers
  2. Find your App ID and copy the Bundle ID
  3. Add it to GitHub secret IOS_BUNDLE_ID
If not set, the workflow will fall back to ANDROID_PACKAGE_NAME or com.example.app
IOS_TEAM_ID Required

Your Apple Developer Team ID (10 characters).

How to get this value
  1. Open Apple Developer Account
  2. Go to Membership and copy your Team ID
  3. Add it to GitHub secret IOS_TEAM_ID
APPLE_TEAM_ID Required

Same Team ID as above. Some workflows use both variables for compatibility.

Value: Use the same value as IOS_TEAM_ID
IOS_CERTIFICATE_BASE64 Required

Your Apple Distribution certificate exported as .p12 and base64-encoded.

How to get this value
  1. Create or confirm an Apple Distribution certificate at Apple Developer Certificates
  2. On a Mac, open Keychain Access and locate your Apple Distribution certificate under "My Certificates"
  3. Right-click → Export → save as ios_distribution.p12 and set a password
  4. Convert to base64:
base64 -i ios_distribution.p12 | pbcopy

Paste into GitHub secret IOS_CERTIFICATE_BASE64

IOS_CERTIFICATE_PASSWORD Required

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.

IOS_PROVISIONING_PROFILE_BASE64 Required

An App Store provisioning profile (.mobileprovision) encoded as base64.

How to get this value
  1. Open Apple Developer Profiles
  2. Create an App Store provisioning profile for your Bundle ID
  3. Download the .mobileprovision file
  4. Convert to base64:
base64 -i YourProfile.mobileprovision | pbcopy

Paste into GitHub secret IOS_PROVISIONING_PROFILE_BASE64

Optional (TestFlight Upload)

APP_STORE_CONNECT_API_KEY_ID Optional Enables TestFlight

Key ID for an App Store Connect API key.

How to get this value
  1. Open App Store Connect Users and Access
  2. Go to IntegrationsApp Store Connect APITeam Keys and generate a key with the App Manager role (only the Account Holder or an Admin can generate keys)
  3. Copy the Key ID and save it as APP_STORE_CONNECT_API_KEY_ID
APP_STORE_CONNECT_API_ISSUER_ID Optional Enables TestFlight

Issuer ID shown on the App Store Connect Keys page.

How to get this value
  1. Open App Store Connect Users and Access
  2. Go to IntegrationsApp Store Connect API and copy the Issuer ID shown on that page
  3. Save it as APP_STORE_CONNECT_API_ISSUER_ID
APP_STORE_CONNECT_API_KEY_BASE64 Optional Enables TestFlight

The downloaded .p8 private key file encoded as base64.

How to get this value
  1. When you create the App Store Connect API key, download the AuthKey_XXXXXXXXXX.p8 file (one-time download)
  2. Convert to base64:
base64 -i AuthKey_XXXXXXXXXX.p8 | pbcopy

Paste into GitHub secret APP_STORE_CONNECT_API_KEY_BASE64

First-time TestFlight uploads require an App Store Connect app record to exist. Create it at App Store Connect (My Apps → + → New App).

Prerequisites & Security

Developer Accounts Required

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 staging and production with environment-protected secrets
  • Limit who can run workflows on main and staging branches
  • 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.