Build and Share a Signed Release APK
Package your React Native app into a signed APK anyone can install.
Introduction
30 Second Summary
You've built an app that works on your computer. But until it's on someone else's phone, it's just a project sitting in a folder.
In this project, you will take your existing React Native restaurant game and package it into a signed release APK. You'll share it via a link that anyone with an Android phone can tap to install.
What You'll Build
Imagine texting a link to the restaurant owner and watching them play your game on their phone within minutes of tapping it.
By the end of this project, you'll have:
- A signed release APK that installs cleanly on any Android phone without debug warnings or error prompts.
- A smaller, optimized app thanks to R8 code shrinking that removes unused code from your build.
- A shareable Google Drive link anyone can open on their phone to download and install your game.
- Secret Mission: Add a custom app icon so your game looks professional in the app drawer.
Are there any prerequisites?
You need a working React Native or Expo app that already runs successfully on an emulator or device. Your project should have an android/ directory with a working Gradle build setup.
Before We Start
Before we dive in, let's be clear on the goal. You have a working restaurant game that runs on your computer. In this project, you'll package it into a signed APK file you can hand to the restaurant owner today.
Generate Your Signing Key
Android refuses to install an app unless it's signed with a cryptographic key. Without one, Gradle can only produce a debug build that shows warnings and can't be shared cleanly.
In this step, you'll generate a signing key, place it inside your project, and tell Gradle where to find it. Once that's done, Gradle has everything it needs to produce a properly signed release build.
In this step, get ready to:
- Generate a PKCS12 keystore using keytool.
- Move the keystore into your project's android directory.
- Add signing credentials to your Gradle properties file.
Generate a PKCS12 keystore with keytool
The keytool utility ships with the JDK that Android Studio bundles. You'll use it to create a cryptographic key pair wrapped in a password-protected keystore file.
- Press the Windows key to open your search bar.
- Type cmd, then right-click Command Prompt and select Run as administrator.
- Navigate to the JDK bin directory bundled with Android Studio by running this command:
cd "C:\Program Files\Android\Android Studio\jbr\bin"
Path not found?
Your Android Studio installation may be in a different location. Check C:\Program Files\Android\Android Studio\jbr\bin first.
If that doesn't exist, the React Native docs reference C:\Program Files\Java\jdkx.x.x_x\bin for a standalone JDK install. Replace x.x.x_x with your installed JDK version number.
- Generate your keystore by running this command:
keytool -genkeypair -v -storetype PKCS12 -keystore my-upload-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
What will keytool ask you?
This command is interactive. It will prompt you for several pieces of information:
- A keystore password (and confirmation). Pick something memorable. You'll need this password again later.
- A key password. Because PKCS12 requires the same password for both, press Enter to reuse the keystore password when prompted.
- Distinguished Name fields: your first and last name, organizational unit, organization, city, state, and two-letter country code. You can type placeholder values for a personal project.
- A final confirmation. Type yes and press Enter to confirm.
What does this command do?
- -genkeypair creates a new public/private RSA key pair.
- -storetype PKCS12 uses the modern keystore format required by JDK 11+.
- -alias my-key-alias gives your key a name so Gradle can reference it later.
- -validity 10000 means the key stays valid for about 27 years.
After you confirm, keytool prints a summary and creates my-upload-key.keystore in the current directory.
Seeing 'keytool' is not recognized?
- Make sure you ran cd into the JDK bin directory first. Keytool only works from its own folder unless Java is on your system PATH.
- Confirm you're running Command Prompt as Administrator.
Help me troubleshoot the keytool command not being recognized on Windows.
Move the keystore into your project
The keystore needs to live inside your project's android\app folder so that Gradle can find it during the build.
- Move the keystore file into your project by running this command (replace the project path with your actual project location):
move my-upload-key.keystore [[PROJECT_PATH="C:\Users\YourName\Desktop\your-project"]]\android\app\
- Confirm the file arrived by running:
dir [[PROJECT_PATH="C:\Users\YourName\Desktop\your-project"]]\android\app\my-upload-key.keystore
You should see my-upload-key.keystore listed with a file size greater than zero.
File not found after the move?
- Double-check that you ran the keytool command from the JDK bin directory. That's where the file was created.
- Make sure your project path uses backslashes and points to an existing android\app folder.
Help me find my keystore file after running keytool on Windows.
Add signing variables to gradle.properties
Gradle needs to know which keystore file to use, which alias to look up, and what password to unlock it with. You store these values in gradle.properties so they're available to every Gradle build.
- Open the file android\gradle.properties in any text editor.
- Add the following four lines at the bottom of the file (replace your-password-here with the password you chose when creating the keystore):
MYAPP_UPLOAD_STORE_FILE=my-upload-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=your-password-here
MYAPP_UPLOAD_KEY_PASSWORD=your-password-here
What do these variables do?
- MYAPP_UPLOAD_STORE_FILE tells Gradle the filename of your keystore (relative to the android/app directory).
- MYAPP_UPLOAD_KEY_ALIAS identifies which key inside the keystore to sign with.
- MYAPP_UPLOAD_STORE_PASSWORD and MYAPP_UPLOAD_KEY_PASSWORD must be the same value because PKCS12 keystores require matching passwords.
- Save gradle.properties.
Keep this password safe
If you ever publish this app to the Google Play Store, you will need this exact keystore and password to push updates. Losing the keystore means you can never update the app under the same listing. Store a backup copy somewhere safe.
Not sure which file to edit?
- The file is at android\gradle.properties inside your project folder. Not android\app\gradle.properties, and not your global ~/.gradle/gradle.properties.
- Make sure you added the lines at the bottom of the file, not inside an existing block.
Help me find the right gradle.properties file to edit for release signing.
Before you run the verification command, what do you think keytool -list will show for a keystore that contains one key?
- Verify everything is wired up by running this command from your project root:
keytool -list -keystore android\app\my-upload-key.keystore
- Enter your keystore password when prompted.
You should see one entry with the alias my-key-alias listed as a PrivateKeyEntry. That confirms your keystore is valid and Gradle will be able to read it.
Seeing a password error?
- Re-check the password you typed. It must match exactly what you entered when generating the keystore.
- If keytool is not recognized, you need to run this from the JDK bin directory or add it to your PATH.
Help me verify my keystore is set up correctly.
Your signing key is ready and Gradle knows where to find it. Next up, you'll configure Gradle to actually use this key when building a release APK.
Configure Gradle for Release Signing
Your keystore is safely stored in your project, and gradle.properties holds the credentials. But Gradle doesn't automatically know to use them.
Without explicit wiring in your build.gradle file, running assembleRelease produces an unsigned APK that Android rejects at install time. In this step, you'll connect the dots so Gradle reads your signing credentials and applies them during every release build.
In this step, get ready to:
- Add a signingConfigs block to your build.gradle file.
- Point the release build type to use your new signing config.
Add the signing config block
The signingConfigs block tells Gradle which keystore file to open and which credentials to use. It reads the values you stored in gradle.properties so your passwords never appear directly in the build script.
- Open android\app\build.gradle in your code editor.
- Scroll down until you find the android { } section.
- Inside the android { } block, after the defaultConfig { } section, add the following code:
signingConfigs {
release {
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
storeFile file(MYAPP_UPLOAD_STORE_FILE)
storePassword MYAPP_UPLOAD_STORE_PASSWORD
keyAlias MYAPP_UPLOAD_KEY_ALIAS
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
}
}
}
What does this code do?
- project.hasProperty('MYAPP_UPLOAD_STORE_FILE') checks that your gradle.properties variables exist before trying to use them. This prevents build errors if the properties are missing.
- storeFile file(MYAPP_UPLOAD_STORE_FILE) points Gradle to your my-upload-key.keystore file in the android\app directory.
- The remaining lines provide the password and alias Gradle needs to open the keystore and access your signing key.
Point the release build type to your signing config
The signing config alone just declares credentials. You still need to tell the release build type to actually use them.
- In the same android\app\build.gradle file, scroll down to find the buildTypes section.
- Find this block:
buildTypes {
release {
// ...existing lines like minifyEnabled, proguardFiles, etc.
}
}
- Inside the release { } block, add this line:
signingConfig signingConfigs.release
- If there is already a signingConfig line inside your release block pointing to something else, replace it with the line above.
What does this line do?
This line connects the dots. It tells Gradle: "When building a release APK, use the credentials defined in signingConfigs.release." Without it, Gradle has the config but never applies it.
- Your buildTypes section should now look something like this:
buildTypes {
release {
// ...your existing release settings...
signingConfig signingConfigs.release
}
}
- Save android\app\build.gradle.
- Scroll through the file and confirm you can see both the signingConfigs { release { } } block (after defaultConfig) and signingConfig signingConfigs.release inside buildTypes.release.
Something look off in build.gradle?
- Make sure signingConfigs is placed inside the android { } block, not outside it. Placing it at the file level causes a Gradle parse error.
- Check that your curly braces are balanced. Every opening { needs a matching }. A missing brace is the most common copy-paste mistake.
- Confirm you added signingConfigs BEFORE buildTypes in the file. Gradle reads top to bottom. If buildTypes references signingConfigs.release before it's defined, the build fails.
Help me fix my build.gradle signing configuration.
✔️ Awesome, I've got everything!
Great. Make sure you've saved android\app\build.gradle and you're ready for the next step.
ⓧ I'd like to double check the full code
Scroll through your android\app\build.gradle and confirm these two additions are present, in this order inside the android { } block:
- The signingConfigs { release { ... } } block appears after defaultConfig and before buildTypes.
- Inside buildTypes { release { } }, the line signingConfig signingConfigs.release is present.
Gradle now knows exactly where your keystore lives and how to unlock it. Next up, you'll run the actual build and produce a signed APK file ready for installation.
Build the Release APK
Your Gradle configuration now knows exactly where to find the keystore and which credentials to use.
This step is the moment of truth. Gradle will bundle your JavaScript, compile the native code, sign it with your key, and shrink the output with R8 code optimization. If anything is misconfigured, the build fails here, and the error message tells you exactly what to fix.
In this step, get ready to:
- Run gradlew assembleRelease to produce a signed APK.
- Confirm the APK file exists at the expected output path.
Run the release build
The assembleRelease Gradle task takes your entire React Native app and produces a single .apk file that is signed, optimized, and ready to install on any Android phone.
- Open a terminal window at your project's root folder (the folder that contains the android directory).
- Move into the android directory and run the release build by running these commands:
Before you run this, do you think the build will succeed on the first try? What might go wrong based on what you configured in the previous steps?
cd android
gradlew assembleRelease
The first build may take a few minutes as Gradle downloads dependencies and compiles native code.
What does this command do?
The gradlew assembleRelease command triggers the full release pipeline. Gradle bundles your JavaScript into a single file, compiles your native Java/Kotlin code, applies your signing key from the keystore, and runs R8 code shrinking to remove unused code.
R8 is Android's built-in optimizer. It removes unused classes and methods, making your APK noticeably smaller than the debug version.
- Wait for the build to finish. You should see a BUILD SUCCESSFUL message in your terminal when it completes.
Build failed?
Read the error message carefully. Here are the most common issues:
- Wrong password in gradle.properties. Open the file and double-check for typos in MYAPP_UPLOAD_STORE_PASSWORD and MYAPP_UPLOAD_KEY_PASSWORD. Both must match the password you chose when generating the keystore.
- Missing keystore file. Confirm the file exists at android\app\my-upload-key.keystore. If you see a "file not found" error, the keystore was not moved to the correct directory.
- Gradle properties not loading. Make sure there is no org.gradle.configureondemand=true line in your gradle.properties file, as it can prevent properties from being read correctly.
Fix the issue and re-run gradlew assembleRelease from the android directory. Help me debug my Gradle build error
Confirm the APK file exists
A successful build places the signed APK at a specific output path inside your project.
- Confirm the APK was created by running this command (you should still be inside the android directory):
dir app\build\outputs\apk\release\
- You should see app-release.apk listed in the output. The file size should be noticeably smaller than your debug APK thanks to R8 code shrinking.
Why is the release APK smaller?
React Native release builds enable R8 (Android's code shrinker) by default. R8 removes unused classes, methods, and resources from your final APK. It also obfuscates the remaining code, making the file both smaller and harder to reverse-engineer.
Don't see app-release.apk?
- Make sure the build actually completed with a BUILD SUCCESSFUL message. If it failed silently, scroll up in your terminal to find the error.
- Check that you ran gradlew assembleRelease (not assembleDebug). The debug variant outputs to a different folder.
Help me find my release APK after running gradlew assembleRelease
You now have a signed, optimized APK ready to install on a real phone. Next up, you'll test it on a physical device and create a shareable link so the restaurant owner can download it.
Test and Share Your APK
You have a signed release APK sitting in your project's build folder. Before you send it to the restaurant owner, you need to prove it actually works on a real phone.
A broken install wastes their time and your credibility. In this step, you'll install the APK on your own device, confirm everything runs smoothly, then upload it to Google Drive so anyone with an Android phone can download your game.
In this step, get ready to:
- Install and test the release APK on a physical Android device.
- Upload the APK to Google Drive with a shareable link.
- Send install instructions so anyone can sideload your game.
Install the release APK on your phone
To install the APK directly from your computer, your Android phone needs USB debugging enabled. This lets your computer push apps to the phone over a USB cable.
- On your Android phone, open Settings.
- Tap About phone.
- Tap Build number seven times until you see the message "You are now a developer!" (you may need to enter your PIN).
- Go back to Settings, then tap System, then Developer options.
- Toggle on USB debugging.
- Connect your phone to your computer with a USB cable.
- If a prompt appears on your phone asking to allow USB debugging, tap Allow.
What if I already have Developer options enabled?
If you've used USB debugging before, you can skip straight to connecting your phone. Just make sure the USB debugging toggle is still on in Developer options.
Before you run the install command, what do you think will happen on your phone? Will you see the app launch automatically, or will you need to open it yourself?
- Install and launch the release build on your connected phone by running this command:
npx react-native run-android --mode=release
What does this command do?
This command builds and installs the release variant of your app directly onto the connected device. It uses the signing configuration you set up in the previous steps, so the APK is fully signed and optimized.
The app launches automatically on your phone once the install finishes. You should see your restaurant game open without any debug banners or yellow warning boxes.
Your restaurant game launches on the phone. Play through it briefly to confirm everything works correctly. No debug banners should appear.
Prefer adb install instead?
If you already built the APK in the previous step and just want to push it to your phone without rebuilding, use this alternative command:
adb install android\app\build\outputs\apk\release\app-release.apk
This installs the pre-built APK directly without running the full Gradle build again.
App not installing on your phone?
Make sure your phone screen is unlocked when you run the command. Some devices block installs while locked.
If you see "no devices/emulators found", your phone isn't being recognized. Check that the USB cable supports data transfer (not just charging) and that you tapped Allow on the USB debugging prompt.
On Windows, you may need to install OEM USB drivers for your phone manufacturer. Check the Android developer docs for your device brand.
help me troubleshoot why my release APK won't install on my physical Android device
Upload to Google Drive and share the link
Your APK works on a real device. Now you need to put it somewhere anyone can download it. Google Drive gives you a free, shareable link that works on any phone.
- Open Google Drive in your browser at drive.google.com.
- Click + New in the top left corner.
- Click File upload.
- Navigate to android\app\build\outputs\apk\release\ inside your project folder and select app-release.apk.
- Wait for the upload to finish. You'll see a confirmation at the bottom of the page.
Now set the sharing permissions so anyone with the link can download the file.
- Right-click app-release.apk in your Google Drive.
- Click Share.
- Under General access, change Restricted to Anyone with the link.
- Click Copy link.
- Click Done.
You now have a shareable link in your clipboard. Open a new browser tab and paste the link to confirm it loads a download page for your APK.
Link showing 'Request access' instead of a download?
Double-check that you changed the access to Anyone with the link, not just added a specific email address. The setting should show a globe icon next to it.
help me fix my Google Drive sharing settings so anyone can download my APK without requesting access
Send install instructions to the restaurant owner
The person receiving your link needs to know how to install an APK. Android phones don't install apps from outside the Play Store by default. The recipient needs to enable sideloading when prompted.
- Send your Google Drive link to the restaurant owner or a friend via text message or chat.
- Include these instructions with your link: "Open this link on your Android phone. Tap Download. When it finishes, tap the file and hit Install. If prompted about unknown apps, tap Settings and enable 'Allow from this source', then go back and tap Install."
Why do they need to allow unknown apps?
Android requires users to navigate to the "Install unknown apps" system settings and explicitly enable app installations from a particular source (like Chrome or Files). This is a security measure that prevents random websites from silently installing apps.
Your recipient only needs to do this once per source app. After that, future APK downloads from the same browser will install without the extra prompt.
Ask the recipient to confirm they can open the app and play your restaurant game. That confirmation is your proof that you just shipped software to a real user.
Secret mission
Add a Custom App Icon
Your game works, but it still has that generic React Native icon in the app drawer. In this secret mission, you'll replace it with a custom icon that makes your game look professional on anyone's home screen.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally and on free Google Drive storage, so there are no ongoing costs.
Back up your keystore
Your my-upload-key.keystore file is critical. If you ever want to publish updates to this app on the Google Play Store, you need this exact keystore file.
Copy it to a secure backup location (a password manager, encrypted USB drive, or a second cloud storage folder) before deleting anything. Once deleted, it cannot be recovered or regenerated.
Resources you used:
- A signing keystore file (my-upload-key.keystore) stored in android\app\.
- A Google Drive shared file (app-release.apk) using your free storage quota.
- Build output artifacts in android\app\build\outputs\apk\release\.
Keep everything running
No action needed. Choose this if you want people to keep downloading and installing your restaurant game.
- Your APK stays on Google Drive for anyone with the link to download. There is no ongoing cost beyond your free storage quota.
- Your keystore file stays in android\app\ ready for future builds. Back it up to a second location so you never lose it.
- Your signing variables remain in android\gradle.properties so you can rebuild at any time.
Pause - I'll come back to this later
Remove public access to the APK but keep all local files so you can re-share later.
- Open Google Drive and find your app-release.apk file.
- Right-click the file, select Share, then change access from Anyone with the link to Restricted.
- Click Done. The shared link stops working immediately, but the file stays in your Drive.
- Keep your keystore file and gradle.properties untouched so you can rebuild and re-share later.
Delete - I don't want to use this again
Remove all project resources and shared files completely.
- Open Google Drive, right-click your app-release.apk file, and select Move to trash. Then open Trash in the left sidebar and click Empty trash to permanently delete it.
- Delete the build output artifacts by opening a terminal in your project root and running:
rmdir /s /q android\app\build\outputs\apk\release
On macOS or Linux, use this command instead:
rm -rf android/app/build/outputs/apk/release
- If you are sure you will never need to update or republish this app, delete the keystore file:
del android\app\my-upload-key.keystore
On macOS or Linux:
rm android/app/my-upload-key.keystore
- Remove the signing variables from android\gradle.properties by deleting the four lines starting with MYAPP_UPLOAD_.
That's a Wrap!
That's a Wrap!
Nice work! You've just shipped your restaurant game to real users by building a signed release APK and sharing it via a link anyone can tap to install.
You've learned how to:
- Generated a PKCS12 signing keystore and configured Gradle to sign release builds automatically with your credentials.
- Built a signed, optimized release APK with R8 code shrinking and tested it on a physical Android device.
- Shared your app via a Google Drive link that anyone can open on their phone to download and install your restaurant game.
- Secret Mission: Added a custom app icon so your game looks professional in the app drawer.
Ready to quiz yourself?