Topic 13 of 15 · React Native

Topic 13 : Generating signed apk

Lesson TL;DRTopic 13: Generating Signed APK 📖 5 min read · 🎯 advanced · 🧭 Prerequisites: redux, firebase Why this matters Before your app can reach a real user through the Play Store — or even be shared as a d...
5 min read·advanced·react-native · android · apk · gradle

Topic 13: Generating Signed APK

📖 5 min read · 🎯 advanced · 🧭 Prerequisites: redux, firebase

Why this matters

Before your app can reach a real user through the Play Store — or even be shared as a direct download — Android requires a digital signature on it. Think of it like a wax seal on an envelope: it proves the APK came from you and hasn't been tampered with. Without it, Android simply won't install the file. In this lesson, we'll go through every step together: creating a keystore, connecting it to Gradle, and building a release-ready signed APK you can actually ship.

What You'll Learn

  • Generate a Java keystore using the keytool command-line utility
  • Configure android/app/build.gradle with signingConfigs for a release build
  • Securely store keystore credentials in android/gradle.properties
  • Build a signed release APK with ./gradlew assembleRelease
  • Verify the signed APK by installing it on a physical Android device

The Analogy

Think of signing an APK like notarising a legal document. A notary stamp does not change the words on the page — it proves the person who signed it is who they claim to be, and that nothing was altered after they signed. Your keystore is the notary seal: it is issued once, kept secret, and referenced every time you produce a new build. Android's package manager checks that stamp before installing anything, and if the seal ever changes between updates, the OS refuses the install. Lose the keystore file and you can never push an update to the same package name — treat it like a password you can never reset.

Chapter 1: Creating a Keystore

A keystore is a binary file that holds one or more private keys. For a production Android app you generate it once and keep it for the life of the app.

Step 1 — Generate a keystore with keytool

keytool ships with every JDK installation. Open a terminal and run:

keytool -genkey -v \
  -keystore my-release-key.keystore \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000 \
  -alias my-key-alias

Flag breakdown:

FlagPurpose
-keystoreOutput filename for the keystore
-keyalg RSAAsymmetric algorithm required by Android
-keysize 2048Key length in bits (2048 is the minimum recommended)
-validity 10000Certificate validity in days (~27 years)
-aliasName used to look up this key inside the keystore

After running the command you will be prompted for:

  1. A keystore password (store this safely — you cannot recover it)
  2. Your name, organisation unit, organisation, city, state, and country code
  3. A key password (can be the same as the keystore password)

When the prompts complete, my-release-key.keystore will appear in the current directory.

Keep this file and both passwords in a secure vault (e.g., 1Password, Bitwarden, or a secrets manager). If you lose either, you can never update your app under the same package name.

Chapter 2: Configuring Gradle for Signing

With the keystore in hand, the next step is wiring it into the Android build system.

Step 1 — Move the keystore file

Copy or move my-release-key.keystore into the android/app/ directory of your React Native project:

mv my-release-key.keystore android/app/

Step 2 — Configure android/app/build.gradle

Open android/app/build.gradle and add a signingConfigs block, then reference it in 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 will still succeed in CI environments where those properties are intentionally absent (e.g., a PR check that only needs a debug build).

Step 3 — Set up android/gradle.properties

Create or open android/gradle.properties and append the four signing properties:

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 entered during keystore generation.

Do not commit gradle.properties to version control if it contains real passwords. Add it to .gitignore and inject values via environment variables in CI. Many teams store only placeholder values in the committed file and override them in their CI secrets vault.

Chapter 3: Building the Signed APK

With signing configured, building the APK is a two-command operation.

Step 1 — Clean the project

Navigate to the android directory and run the clean task to remove any stale build artefacts:

cd android
./gradlew clean

Step 2 — Build the signed APK

Run the assembleRelease Gradle task:

./gradlew assembleRelease

Gradle will compile the JavaScript bundle, strip debug symbols, apply ProGuard/R8 minification (if enabled), and sign the resulting APK with your keystore. The process typically takes 2–5 minutes on a modern machine.

When the build succeeds you will see output similar to:

BUILD SUCCESSFUL in 3m 24s

The signed APK is written to:

android/app/build/outputs/apk/release/app-release.apk

Chapter 4: Verifying the Signed APK

Generating the file is not the finish line — verifying that the APK installs and runs correctly is.

Step 1 — Install the APK on a device

Transfer app-release.apk to an Android device via USB, ADB, or a file-sharing method. Before installing:

  1. On the device, go to Settings → Security (or Settings → Apps → Special app access on Android 8+).
  2. Enable Install from Unknown Sources (or grant the permission to the specific file manager app you are using).

Install via ADB directly 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 your core user flows:

  • Confirm all screens load without the red error overlay
  • Verify network calls reach your backend (the release bundle points to production, not the Metro dev server)
  • Check Redux state persistence and Firebase integrations behave as expected in release mode

If the app crashes at launch, enable adb logcat to inspect the error:

adb logcat *:E

Release builds do not show the in-app red-screen error overlay, so logcat is your primary debugging tool at this stage.

🧪 Try It Yourself

Task: Generate a keystore and produce a signed release APK for your existing React Native project.

  1. Run the keytool command to create my-release-key.keystore with a 2048-bit RSA key, validity of 10000 days, and alias my-key-alias.
  2. Move the keystore to android/app/.
  3. Update android/app/build.gradle with the signingConfigs block shown in Chapter 2.
  4. Add the four MYAPP_RELEASE_* properties to android/gradle.properties.
  5. Run the build:
cd android && ./gradlew clean && ./gradlew assembleRelease

Success criterion: The terminal prints BUILD SUCCESSFUL and the file android/app/build/outputs/apk/release/app-release.apk exists. Install it on a device or emulator with adb install and confirm the app launches without errors.

🔍 Checkpoint Quiz

Q1. Why does Android require a signed APK rather than an unsigned one?

A) It compresses the APK to reduce file size
B) It provides cryptographic proof of the developer's identity and ensures the code was not tampered with after signing
C) It enables Google Play to run automated tests on the app
D) It converts JavaScript bundles into native bytecode

Q2. Given the following gradle.properties excerpt, what value will Gradle pass to storeFile in build.gradle?

MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=s3cr3t
MYAPP_RELEASE_KEY_PASSWORD=s3cr3t

A) my-key-alias
B) s3cr3t
C) my-release-key.keystore
D) android/app/build.gradle

Q3. What happens if you lose your my-release-key.keystore file after publishing version 1.0 of your app?

A) You can regenerate an identical keystore with keytool using the same alias
B) Google Play will re-sign the APK automatically on the next upload
C) You cannot push updates to the same package name; users would need to uninstall and reinstall a new package
D) The existing app continues to work but new installs are blocked for 30 days

Q4. A teammate runs ./gradlew assembleRelease and gets BUILD SUCCESSFUL, but the APK crashes immediately on launch with no red-screen error. What is the most likely debugging step?

A) Re-run ./gradlew clean with the --debug flag
B) Check adb logcat *:E for the native crash or JS exception in the release build
C) Disable signingConfig in build.gradle and rebuild
D) Reinstall the JDK and regenerate the keystore

A1. B — Android's package manager verifies the developer certificate on every install and update; if the signature does not match the previously installed version, the update is rejected.

A2. C — MYAPP_RELEASE_STORE_FILE maps to storeFile, which receives the string my-release-key.keystore.

A3. C — The keystore contains the unique private key that proves ownership of the package. Without it, the new APK cannot be signed with the same certificate, so the Play Store treats it as a different app.

A4. B — Release builds suppress the in-app red-screen overlay. adb logcat *:E streams native and Java exceptions to the terminal, which is the standard way to diagnose crashes in production builds.

🪞 Recap

  • The keytool command generates a keystore file containing the RSA private key used to sign your APK; keep this file and both passwords in a secure vault permanently.
  • android/app/build.gradle must declare a signingConfigs.release block that references the four MYAPP_RELEASE_* Gradle properties, then that config must be assigned inside buildTypes.release.
  • Credentials are stored in android/gradle.properties and should never be committed to version control with real values.
  • Running ./gradlew clean && ./gradlew assembleRelease produces the signed APK at android/app/build/outputs/apk/release/app-release.apk.
  • Always verify the signed APK on a physical device using adb install and inspect crashes via adb logcat before submitting to the Play Store.

📚 Further Reading

Like this topic? It’s one of 15 in React Native.

Block your seat for ₹2,500 and join the next cohort.