Topic 13: Generating Signed APK
📖 5 min read · 🎯 Advanced · 🧭 Prerequisites: firebase, networking
Why this matters
Up until now, you've been running your React Native app in dev mode — on a simulator or a USB-connected phone. That's fine for building, but it's not something you can share with anyone. To put your app on a real device, send it to a tester, or upload it to the Play Store, you need a signed APK. Think of signing like putting your official stamp on a document — Android uses it to verify the app actually came from you. Skip this step and Android simply won't install it. So let's generate a keystore, configure the build, and produce a release APK you can actually hand to someone.
What You'll Learn
- Generate a keystore using the
keytoolcommand - Configure
android/app/build.gradlewith signing configs - Store credentials securely in
android/gradle.properties - Build and verify a signed release APK using Gradle
The Analogy
Think of a signed APK like a notarized legal document. The document itself is your app; the notary stamp is the cryptographic signature from your keystore. Without that stamp, Android's security model refuses to trust it — and rightly so. Just as a notary seal proves the document came from you and hasn't been tampered with since, an APK signature proves authorship and integrity to both the OS and app stores. Lose the keystore and you can never issue a legitimate update under the same identity — same as losing the only notary seal in existence.
Chapter 1: Creating a Keystore
A keystore is a binary file that holds a private key used to sign your APK. You generate it once and keep it safe forever — losing it means you cannot push updates to the same published app.
Step 1: Generate the keystore file
Run the keytool command (bundled with the JDK) in your terminal:
keytool -genkey -v -keystore my-release-key.keystore -keyalg RSA -keysize 2048 -validity 10000 -alias my-key-alias
The flags in detail:
| Flag | Purpose |
|---|---|
-keystore my-release-key.keystore | Output file name |
-keyalg RSA | Key algorithm |
-keysize 2048 | Key strength in bits |
-validity 10000 | Validity in days (~27 years) |
-alias my-key-alias | Internal alias for this key |
You will be prompted interactively for your name, organization, city, country, and a keystore password. After completing the prompts, my-release-key.keystore is created in your current directory.
Security note: Back up this file to a secure location immediately. Never commit it to version control.
Chapter 2: Configuring Gradle for Signing
With the keystore in hand, you need to wire it into the Android build system so Gradle uses it when assembling the release build.
Step 1: Move the keystore file
Place my-release-key.keystore inside the android/app directory of your React Native project:
mv my-release-key.keystore android/app/
Step 2: Configure android/app/build.gradle
Add a signingConfigs block and reference it from buildTypes.release:
android {
...
defaultConfig { ... }
signingConfigs {
release {
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}
The project.hasProperty(...) guard means the build won't fail on CI machines where the properties are absent — it degrades gracefully to unsigned.
Step 3: Set up android/gradle.properties
Create or update android/gradle.properties with your keystore credentials:
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=your-store-password
MYAPP_RELEASE_KEY_PASSWORD=your-key-password
Replace your-store-password and your-key-password with the actual passwords you set during keystore generation. This file should be in .gitignore — treat it like an .env file containing secrets.
Chapter 3: Building the Signed APK
With signing configured, Gradle can assemble the release variant and sign it automatically.
Step 1: Clean the project
Navigate into the android directory and run a clean to remove stale build artifacts:
cd android
./gradlew clean
Step 2: Build the signed APK
./gradlew assembleRelease
Gradle will compile, package, and sign the APK using the keystore referenced in gradle.properties. When the build succeeds, your signed APK is at:
android/app/build/outputs/apk/release/app-release.apk
flowchart LR
A[keytool\ngenerate keystore] --> B[Move to\nandroid/app/]
B --> C[Configure\nbuild.gradle]
C --> D[Set\ngradle.properties]
D --> E[gradlew clean]
E --> F[gradlew assembleRelease]
F --> G[app-release.apk\nsigned & ready]
Chapter 4: Verifying the Signed APK
Shipping an untested release build is a gamble. Always verify the APK on a physical device before distribution.
Step 1: Install the APK on a device
Transfer app-release.apk to your Android device via USB, ADB, or a file sharing app. To install it directly outside the Play Store, enable Install from Unknown Sources in your device's security settings (Settings → Security → Unknown Sources or Settings → Apps → Special App Access → Install Unknown Apps depending on Android version).
Install via ADB if the device is connected:
adb install android/app/build/outputs/apk/release/app-release.apk
Step 2: Test the application
Launch the installed app and run through the core user flows. Confirm:
- The app launches without crashing
- All features work as expected (network calls, auth, navigation)
- No debug/developer overlays are visible
- Performance feels production-ready (no Metro bundler dependency)
🧪 Try It Yourself
Task: Generate a keystore and produce a signed APK for a new or existing React Native project.
- Run the
keytoolcommand to createmy-release-key.keystore - Move it to
android/app/ - Add the
signingConfigsblock toandroid/app/build.gradle - Populate
android/gradle.propertieswith your credentials - Run
./gradlew clean && ./gradlew assembleReleasefrom theandroid/directory
Success criterion: The file android/app/build/outputs/apk/release/app-release.apk exists after the build, and you can install and launch it on a physical Android device or emulator without errors.
# Quick verification: confirm the APK exists and check its size
ls -lh android/app/build/outputs/apk/release/app-release.apk
🔍 Checkpoint Quiz
Q1. Why is it critical to back up your my-release-key.keystore file, and what happens if you lose it?
Q2. Given the following snippet in build.gradle, what happens when MYAPP_RELEASE_STORE_FILE is NOT set as a Gradle property?
signingConfigs {
release {
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
}
A) The build fails immediately with a missing property error
B) The signingConfigs.release block is applied but empty, so the APK is unsigned
C) The if block is skipped, leaving the signing config unconfigured, and the build degrades gracefully
D) Gradle prompts you interactively for the missing values
Q3. Where is the signed APK located after a successful ./gradlew assembleRelease run?
A) android/app/release/app-release.apk
B) android/app/build/outputs/apk/release/app-release.apk
C) android/build/release/app-release.apk
D) android/app/outputs/release/app-release.apk
Q4. A teammate checks android/gradle.properties into the public GitHub repository. What is the risk, and how would you fix it?
A1. If you lose the keystore, you cannot publish updates to an app already on the Play Store under that signing identity — the store requires all updates to be signed with the same key as the original release. You would have to publish a brand-new app. Always store the keystore in a secure, backed-up location (password manager, encrypted cloud storage).
A2. C — The if (project.hasProperty(...)) guard skips the inner assignments, leaving signingConfigs.release empty. The build continues but the APK will be unsigned or use a debug key, which is useful for CI pipelines that don't have access to production credentials.
A3. B — android/app/build/outputs/apk/release/app-release.apk is the standard Gradle output path for the assembleRelease task.
A4. The risk is that the keystore passwords and key alias are now public, allowing anyone to sign APKs impersonating your app. The fix: immediately rotate passwords if possible, add android/gradle.properties to .gitignore, and use CI environment variables or a secrets manager (GitHub Actions secrets, Fastlane Match, etc.) to inject credentials at build time.
🪞 Recap
- A keystore holds the private key that signs your APK; generate it once with
keytooland back it up permanently. - Signing configuration lives in
android/app/build.gradleundersigningConfigs.release, referenced frombuildTypes.release. - Keystore credentials belong in
android/gradle.properties, which must never be committed to version control. - Run
./gradlew clean && ./gradlew assembleReleaseto produce the signed APK atandroid/app/build/outputs/apk/release/app-release.apk. - Always install and smoke-test the APK on a real device before distributing.
📚 Further Reading
- Android Developer — Sign your app — the source of truth on APK signing, key management, and Play App Signing
- React Native — Publishing to Google Play Store — React Native's official guide covering the full release pipeline
- Fastlane Supply — automate the signing, building, and upload process for CI/CD pipelines
- ⬅️ Previous: Networking
- ➡️ Next: React Forms