How to Build a Signed APK from a Capacitor Project Without Android Studio (2026)
Want to see your own site as an app? Paste its address: the free preview shows it in a phone frame. No account needed.
If you have a Capacitor project that already contains an android/ folder, you do not need Android Studio on your own machine to ship it — but something still has to run the Gradle build with a full Android SDK. Build your web assets and run npx cap sync android (both need only Node.js), then let a CI runner such as GitHub Actions run Gradle, sign the output with your release keystore, and hand back an APK for sideloading plus an AAB for Google Play.
A note on our own scope before you read further: Code2Native's source builds do not compile Capacitor projects yet — Capacitor is on our waitlist, and a Capacitor upload stops with an explanation. If your app also works as a deployed website, our URL wrapper is the no-toolchain route today. This guide covers the Capacitor path itself: which half of the build stays on your machine, how the Gradle half moves to CI, how signing works, and the honest alternatives.
The real problem: every Capacitor app is two projects stapled together
A Capacitor app is a web project (Vite, Angular CLI, whatever) plus a completely ordinary Android Gradle project living in android/. The two halves have wildly different toolchain demands, and that asymmetry is the whole story.
The web half is easy. npm run build compiles your app into dist/, and npx cap sync android copies those assets into android/app/src/main/assets/public and wires each installed plugin into the Gradle files. Any machine with Node.js can do this.
Then you hit the other half, and the machine that just built your web app perfectly greets you with this:
Fixing that means installing the Android SDK, a matching JDK, and accepting a pile of licenses — in practice, installing Android Studio. And the versions are a moving target. As of mid-2026, a Capacitor 7 android/ folder expects Java 21 with compile/target SDK 35, while Capacitor 8 raises the floor again to SDK 36, AGP 8.13 and Gradle wrapper 8.14.x — effectively a current Android Studio install. Meanwhile a project scaffolded in the Capacitor 5 era assumes JDK 17 and an older AGP. Set up your machine for one and the other breaks. This treadmill is exactly why npx cap migrate exists.
Capacitor does ship a CLI escape hatch — npx cap build android with --keystorepath / --androidreleasetype flags — but it still requires the full local SDK underneath, its signing defaults to the older jarsigner tool, and the issue tracker has long-running reports (ionic-team/capacitor #7366, #7054) of the apksigner path producing an unsigned APK while claiming success. It automates the last step of a working local setup; it does not replace the setup.
The vendor CI option is closing: Ionic announced in February 2025 that Appflow — the official cloud build service for Capacitor — is no longer sold to new customers, with existing access ending December 31, 2027. If you searched for this article because Appflow is off the table, that instinct is correct.
Moving the Gradle half to a CI runner
The honest division of labor: the Node half runs anywhere, the Gradle half needs a machine with the Android SDK. GitHub's hosted Ubuntu runners ship with the Android SDK preinstalled, so a single workflow can do both halves: install dependencies, build the web assets, sync them into android/, set up the JDK your Capacitor major expects, restore your keystore from repository secrets, and run Gradle.
# .github/workflows/android-release.yml
name: android-release
on: workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci && npm run build
- run: npx cap sync android
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21 # 17 for a Capacitor 5/6-era project
- name: Restore the release keystore
run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > android/app/release.jks
- name: Build a signed APK and AAB
working-directory: android
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: ./gradlew assembleRelease bundleRelease
- uses: actions/upload-artifact@v4
with:
name: android-release
path: |
android/app/build/outputs/apk/release/*.apk
android/app/build/outputs/bundle/release/*.aabGradle only signs a release build if android/app/build.gradle has a release signing config. Read the secrets from the environment so nothing sensitive is committed:
android {
signingConfigs {
release {
storeFile file("release.jks")
storePassword System.getenv("KEYSTORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}Step-by-step: Capacitor project to signed APK/AAB
Check the web build and webDir
npm ci && npm run build must succeed, and its output folder must match webDir in your Capacitor config (usually dist or www).
Commit the android/ platform
If you've never added it, run npx cap add android once and commit the folder. Keep android/app/build/ and android/.gradle/ out of Git.
Store the keystore as secrets
Base64-encode your .jks (base64 -w0 release.jks) and add it with the alias and both passwords as repository secrets. If the app is already on Google Play, this must be the key Play expects — updates signed with a different key are rejected.
Add the workflow and the signing config
The two snippets above. Match java-version to your Capacitor major.
Run it, then verify the signature
Download the artifact, then check the APK really is signed with your key: apksigner verify --print-certs app-release.apk. Sideload the APK to test; upload the AAB to Google Play.
CI vs the honest alternatives
Competitor details below are accurate to the best of our knowledge as of mid-2026 — verify current terms before committing.
| Path | What you set up & maintain | Cost | The catch |
|---|---|---|---|
| Local Android Studio | Studio + SDK + JDK matched to your Capacitor major; re-matched on every upgrade | Free (plus disk, plus time) | Best for daily native-plugin development; overkill if you just need release binaries |
| GitHub Actions | The workflow above, keystore as base64 secrets, a signing config in build.gradle | Free for public repos; minutes metered on private | You own the YAML; the first working signing step usually takes an afternoon |
| Ionic Appflow | Vendor dashboard, polished Capacitor-native pipeline, live updates | Not sold to new customers | Winding down — existing customers keep access until Dec 31, 2027; no new signups |
| Code2Native URL wrapper | Nothing local; paste the deployed URL of the web build | Preview free; App license $69 one-time | Loads your live site in a WebView — no bundled assets or Capacitor plugins. Capacitor source builds are on our waitlist |
One concession worth spelling out: if your team already has a working CI pipeline with signing configured, keep it. And if the only reason you reached for Capacitor was to get an installable app around a web build, compare it with wrapping the deployed site — our Capacitor vs native comparison walks through that fork.
When this approach is the wrong tool
- Your app is React Native or Expo. Different toolchain entirely — Expo's EAS Build is the intended cloud path for Expo code.
- Your Ionic project has no android/ folder. An Ionic app on legacy Cordova needs Cordova's own build. If it is Capacitor underneath, you're one
npx cap add androidaway from the workflow above. - You're actively writing a custom native plugin. Iterating on Kotlin/Java code needs a local emulator, breakpoints, and instant rebuilds. Install Android Studio for development; use CI for releases.
- You don't have a Capacitor project at all. If what you actually have is a deployed website, wrapping it is a simpler, cheaper path than adopting Capacitor first.
FAQ
Do I need to run npx cap sync before Gradle runs?
Yes. Gradle compiles your android/ project as-is, so the web assets must already be synced into it. The good news: sync needs only Node.js, which is why the workflow runs it right before the Java setup.
My project fails locally with “SDK location not found” — will it build in CI?
Usually, because that error is about your machine, not your code: Gradle can't find an Android SDK via ANDROID_HOME or local.properties, and GitHub's hosted Ubuntu runners set ANDROID_HOME for you. Real code errors — a plugin that doesn't compile, a bad Gradle script — still fail, with the full log to show you why.
Why not just use npx cap build android?
If you have a fully working local SDK, you can — it drives Gradle and signs in one command. But it requires that local setup to exist, defaults to legacy jarsigner signing, and has open issues where the apksigner mode reports success while emitting an unsigned APK. It automates the last mile of a configured machine; CI is how you get a configured machine you don't maintain.
Do I need an APK or an AAB for Google Play?
Google Play has required the AAB format for new apps since August 2021; the APK is what you sideload onto devices for testing and direct distribution. The workflow above builds both from the same run, signed with the same key, so you don't have to choose in advance.
Is Ionic Appflow still an option for Capacitor cloud builds?
Only if you're already a customer. Ionic announced in February 2025 that new sales of Appflow are discontinued, and existing customers keep access through December 31, 2027 while the product winds down. As of mid-2026 you cannot sign up, which is why Capacitor teams are re-evaluating their build setup now rather than at the deadline.
Can Code2Native build my Capacitor project?
Not yet. Our source builds compile Flutter and native Android (Gradle) projects; Capacitor, React Native, Ionic, and Expo are on the waitlist, and a Capacitor upload stops with an explanation rather than a half-working build. If your app runs as a deployed website, wrapping that URL works today.
Does your app also run as a website?
Then you can skip the native project entirely: paste the deployed URL and get a Preview APK on your phone in minutes, free. The release APK and AAB come with a one-time App license.
See your website as an app
The preview loads your live site in a phone frame. When it looks right, open an account and build it. Build one Preview APK with a Code2Native watermark. No card required.
Code2Native Engineering
Engineering team
Written by the Code2Native engineering team — the people who build and operate the cloud build pipeline.