Building mobile apps with Apache Cordova means you can write once and ship to both Android and iOS. That is a huge win for teams short on resources. But the build step can become a bottleneck. Running cordova build android and cordova build ios on your local machine for every commit is slow, error-prone, and ties up your computer. Automating Cordova builds with GitHub Actions turns this repetitive task into a hands-free pipeline. Your team pushes code, and GitHub spins up a fresh environment, compiles your app, and hands you a signed APK or IPA. No manual steps, no forgotten signing keys, no “works on my machine” moments.
By the end of this guide, you will have a working GitHub Actions workflow that automatically builds signed Android and iOS apps from your Cordova project. You will learn how to set up secrets for certificates and provisioning profiles, cache dependencies to speed up builds, and handle common pitfalls. This approach saves hours each week and ensures every build is consistent and repeatable.
Why Automate Cordova Builds with GitHub Actions
Manual builds are fine for a small project with infrequent updates. Once you have a team pushing multiple times a day, the pain grows. Here is what automation gives you:
- Consistent environments – Every build runs on the same Ubuntu or macOS runner. No more “it worked on my machine.”
- Immediate feedback – A broken build shows up in a pull request check, not after you have already merged.
- Faster releases – With signing integrated, you can generate a release candidate with one push.
- Security – Keystore passwords and Apple certificates stay in encrypted GitHub Secrets, not on anyone’s laptop.
- Audit trail – Every build is logged, timestamped, and linked to a commit.
If you are new to CI/CD for hybrid apps, you might also want to read about enhance your Cordova apps with advanced debugging techniques to catch issues before they reach the build server.
Prerequisites
Before writing your workflow file, make sure you have the following ready:
- A Cordova project hosted on GitHub. Your
config.xmlandwwwfolder should be committed. - A GitHub account with Actions enabled (free tier includes 2,000 minutes per month for private repos).
- For Android: a keystore file (
.jksor.keystore) and the associated passwords. If you do not have one, generate it withkeytool. - For iOS: an Apple Developer account, a distribution certificate (
.p12), and a provisioning profile (.mobileprovision). You will need a macOS runner for iOS builds. - Basic familiarity with YAML syntax.
Setting Up Your First GitHub Actions Workflow
A workflow is a YAML file stored in .github/workflows/ inside your repository. Let us build one step by step.
Step 1: Define the Trigger and Platform
Start by naming the workflow and telling it when to run. For this example, we will run on every push to the main branch and on pull requests.
name: Build Cordova App
on:
push:
branches:
- main
pull_request:
branches:
- main
Step 2: Choose a Runner and Check Out Code
You need a Linux runner for Android and a macOS runner for iOS. You can use a matrix strategy to build both in parallel.
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
include:
- os: ubuntu-latest
platform: android
- os: macos-latest
platform: ios
steps:
- uses: actions/checkout@v4
Step 3: Install Dependencies
Cordova requires Node.js, the Cordova CLI, and platform-specific SDKs. Use a community action to set up Node, then install Cordova globally.
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install Cordova CLI
run: npm install -g cordova
- name: Install project dependencies
run: npm ci
For Android, the ubuntu-latest runner already has the Android SDK. However, you may need to set the ANDROID_HOME environment variable. For macOS runners, Xcode is pre-installed but you must install CocoaPods if your project uses plugins that rely on it.
- name: Install CocoaPods (macOS only)
if: runner.os == 'macOS'
run: |
sudo gem install cocoapods
pod repo update
Step 4: Add the Platforms
Add the Android and iOS platforms to your Cordova project. This step also restores the platforms if they are already in package.json.
- name: Add platforms
run: |
cordova platform add android --no-telemetry
if [ "${{ matrix.platform }}" == "ios" ]; then cordova platform add ios --no-telemetry; fi
Step 5: Inject Signing Secrets
For Android, you need to Base64 encode your keystore file and store it as a secret. Then decode it during the build.
- name: Decode Android keystore
if: matrix.platform == 'android'
run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > release.keystore
For iOS, you will need the certificate and provisioning profile. Store them as secrets as well and import them into the keychain.
- name: Setup iOS signing (macOS only)
if: runner.os == 'macOS'
env:
IOS_CERTIFICATE: ${{ secrets.IOS_CERTIFICATE_BASE64 }}
IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo "$IOS_CERTIFICATE" | base64 --decode > cert.p12
echo "$IOS_PROVISIONING_PROFILE" | base64 --decode > profile.mobileprovision
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security import cert.p12 -k build.keychain -P "$KEYCHAIN_PASSWORD" -T /usr/bin/codesign
security default-keychain -s build.keychain
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
Step 6: Build and Archive
Now assemble the app. For Android, output an APK or AAB. For iOS, create an .xcarchive and export an IPA.
- name: Build Android
if: matrix.platform == 'android'
run: |
cordova build android --release -- --keystore=release.keystore \
--storePassword=${{ secrets.ANDROID_STORE_PASSWORD }} \
--alias=${{ secrets.ANDROID_KEY_ALIAS }} \
--password=${{ secrets.ANDROID_KEY_PASSWORD }}
- name: Build iOS
if: matrix.platform == 'ios'
run: |
cordova build ios --release --device \
--codeSignIdentity="iPhone Distribution" \
--developmentTeam=${{ secrets.IOS_TEAM_ID }}
xcodebuild -workspace platforms/ios/YourApp.xcworkspace \
-scheme YourApp -configuration Release \
-archivePath build/YourApp.xcarchive archive \
-allowProvisioningUpdates
xcodebuild -exportArchive -archivePath build/YourApp.xcarchive \
-exportPath build/ -exportOptionsPlist exportOptions.plist \
-allowProvisioningUpdates
Step 7: Upload Artifacts
Make the build output available as a downloadable artifact so you can install it on a device or submit to stores.
- name: Upload Android APK
if: matrix.platform == 'android'
uses: actions/upload-artifact@v4
with:
name: android-app
path: platforms/android/app/build/outputs/apk/release/*.apk
- name: Upload iOS IPA
if: matrix.platform == 'ios'
uses: actions/upload-artifact@v4
with:
name: ios-app
path: build/*.ipa
The full workflow file can be extended with caching, linting, and tests. If you want to learn more about plugin compatibility, check out mastering Cordova plugin development for cross-platform compatibility.
Common Pitfalls and How to Avoid Them
Even with a solid workflow, a few gotchas can trip you up. Here is a table of frequent mistakes and solutions.
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Build fails with “No suitable Android SDK found” | GitHub’s Ubuntu runner has multiple Android SDK versions; the environment variable may be missing. | Add echo "ANDROID_HOME=$ANDROID_HOME" to your step and ensure ANDROID_HOME is set. You can use the android-actions/setup-android action. |
| iOS build fails on signing | The certificate or provisioning profile is expired or not installed properly. | Verify the certificate date, re-upload secrets, and remember that macOS runners have a 7-day limit for Xcode certificates. |
| Node modules not found | Dependencies are not installed because npm ci was skipped after caching. |
Use actions/cache for node_modules but ensure npm ci runs on cache miss. |
| Cordova plugin fails on iOS | A plugin expects CocoaPods but pod repo update did not run. |
Add the CocoaPods step shown earlier. If your plugin requires a specific iOS deployment target, set it in config.xml. |
| Secrets exposed in logs | Running a script that echoes a secret value. | Never use echo ${{ secrets.NAME }} directly. Instead, pass secrets as environment variables. |
Expert tip: Always test your workflow with a small, non-production app first. Use a throwaway branch and a test keystore. Once you see the green check mark, move to your real signing credentials.
Caching Dependencies to Speed Up Builds
Cordova builds can take 10–15 minutes per platform. Caching Node modules and platform SDKs cuts that down significantly. Add this before any install steps:
- uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
platforms/android/.gradle
~/.gradle/caches
key: ${{ runner.os }}-${{ hashFiles('package-lock.json', 'config.xml') }}
restore-keys: |
${{ runner.os }}-
For iOS, you can also cache the DerivedData folder and CocoaPods spec repo. The exact paths depend on your project. Experiment and adjust.
When to Use a Separate Workflow for Release Builds
If your main branch flow is for debug builds, consider a second workflow that triggers only on tags or manual dispatch. This release workflow would include signing with your production certificates and possibly upload to app stores. For a deeper look at deployment, see streamlining app deployment for Apache Cordova projects.
Set up a matrix that runs only on macOS for iOS and Ubuntu for Android, and add steps to sign with your store credentials. You can then use actions like r0adkll/upload-google-play for Android or apple-actions/upload-testflight-build for iOS.
Testing Your Pipeline
After you push your workflow file, go to the Actions tab in your GitHub repository. You will see the workflow kick off. Watch the logs for each step. Common errors include:
- Missing secrets – the build will fail with “secret not found”.
- Incorrect keystore path – update the path in the build command.
- Platform mismatches – ensure you only build iOS on macOS.
Once the build succeeds, download the artifact and install it on your device. If you see any runtime issues, those are often related to plugins. You can debug them using essential debugging tips for hybrid Cordova applications.
Making Automation a Habit
Setting up automated Cordova builds with GitHub Actions is one of those investments that pays off on day two. You stop wasting time on manual steps. Your team gains confidence because every build is created the same way. And when a build breaks, you know immediately what changed.
Start with a simple workflow that builds one platform. Add the second platform after it passes. Then introduce caching, signing, and artifact uploads. Before long, you will wonder how you ever lived without it.
Go ahead and create that .github/workflows/build.yml file now. Your future self will thank you.