Android Studio is a fine IDE, but it is not required to produce a store-ready Android build. The generated android/ folder in a Capacitor project contains a Gradle wrapper that builds and signs from any terminal. That matters for Linux servers without a display, for Windows machines where Studio is slow, and for CI.
This guide builds a signed AAB with the command line only, then shows the cloud variant with Capgo Build for teams that do not want keystores on laptops.
Prerequisites
- JDK 21 (
java -versionprints 21). - Android command line SDK with
platform-tools,build-toolsand the platform matching your Capacitor version (API 36 for Capacitor 8, API 35 for Capacitor 7). The Linux guide and Windows guide cover installation. ANDROID_HOMEexported, orandroid/local.propertieswithsdk.dir=....- A Capacitor app with
android/generated bybunx cap add android.
1) Create the signing keystore
keytool is part of the JDK on every platform:
keytool -genkeypair -v \
-keystore release.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias release
Answer the prompts and choose a store password and a key password. Store the file and both passwords in your password manager or secret vault now. Never commit release.jks.
Enable Play App Signing when you create the app in Play Console. Google then keeps the final signing key and your keystore becomes an upload key, which can be reset if lost.
2) Tell Gradle how to sign
Keep secrets out of build.gradle. Create android/keystore.properties (gitignored):
storeFile=/absolute/path/to/release.jks
storePassword=your-store-password
keyAlias=release
keyPassword=your-key-password
Then reference it in android/app/build.gradle:
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file("keystore.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
signingConfigs {
release {
if (keystorePropertiesFile.exists()) {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
}
}
}
Add android/keystore.properties and *.jks to .gitignore.
3) Bump the version
Google Play rejects uploads with a versionCode it has seen before. Edit android/app/build.gradle:
defaultConfig {
versionCode 12
versionName "1.4.0"
}
Teams usually derive versionCode from CI run numbers or from the git commit count to avoid manual edits.
4) Build the AAB from the terminal
bun run build
bunx cap sync android
cd android
./gradlew bundleRelease
On Windows use .\gradlew.bat bundleRelease. The signed bundle is written to android/app/build/outputs/bundle/release/app-release.aab.
Want an APK for direct installs with adb? ./gradlew assembleRelease produces app-release.apk under outputs/apk/release/. Install it with adb install; the adb install guide covers the edge cases.
5) Verify the signature
$ANDROID_HOME/build-tools/36.0.0/apksigner verify --print-certs app/build/outputs/apk/release/app-release.apk
For an AAB, jarsigner -verify -verbose app-release.aab confirms the signature and the certificate fingerprint. Compare the SHA-256 with the one shown in Play Console under App integrity.
6) Upload to Google Play from the command line
Manual upload through Play Console works. For automation, create a service account in Google Cloud, grant it access in Play Console under Users and permissions, and download its JSON key. Then upload with the Google Play Developer API or a tool such as fastlane supply:
bundle exec fastlane supply --aab android/app/build/outputs/bundle/release/app-release.aab \
--track internal --json_key play-service-account.json --package_name com.example.app
The first upload of a new app must be done manually in Play Console; the API only accepts builds for apps that already exist.
7) The cloud alternative: Capgo Build for Android
Everything above works, but it leaves a keystore on every machine that builds. Capgo Build keeps the keystore in an encrypted vault, builds from the prepared android/ project you upload, and can push the AAB to a Play track.
Save credentials once:
bunx @capgo/cli@latest build credentials save --platform android \
--keystore ./release.jks \
--keystore-alias "release" \
--keystore-key-password "key-password" \
--keystore-store-password "store-password" \
--play-config ./play-service-account.json
Or let bunx @capgo/cli@latest build init --platform android guide you. Then every release is:
bun run build
bunx cap sync android
bunx @capgo/cli@latest build request com.example.app --platform android --build-mode release
Logs stream to the terminal. Add --output-upload to get a download link for the AAB or APK, and --ai-analytics to have a failed build explained automatically. The same command runs from Windows, Linux, macOS and CI. A GitHub Actions example lives in the Android build guide.
Because Capgo receives only the prepared native project, private npm registries and web build secrets stay in your environment.
8) Stop rebuilding for web changes
A signed native build is needed for plugin, permission, icon and Capacitor upgrades. For everything in dist/, Capgo Live Updates push the new web bundle directly to installed apps:
bunx @capgo/cli@latest bundle upload --channel production
That removes most Gradle runs from your week.
Troubleshooting
SDK location not found: setANDROID_HOMEor createandroid/local.properties.Keystore was tampered with, or password was incorrect: wrong store password, or the file was corrupted by a CRLF conversion in Git. Mark*.jksas binary in.gitattributesif it must be versioned, which it should not be.Unsupported class file major version: the JDK is newer or older than 21.- Play rejects the AAB: “signed with a different key”: Play App Signing expects your upload key. Use the keystore registered in Play Console, or request an upload key reset.
- Build is slow on Windows: exclude
~/.gradleand the SDK from Defender, and keep the project on a short local path. - Out of memory in Gradle: add
org.gradle.jvmargs=-Xmx4gtoandroid/gradle.properties.
Summary
A Capacitor Android release needs a JDK, the SDK and the Gradle wrapper, not Android Studio. Create the keystore with keytool, sign through keystore.properties, build with bundleRelease, and upload with the Play API. When you want the keystore out of laptops and the same command on every OS, Capgo Build does the signing and upload from the prepared project.