Introduction
Mobile teams often waste hours wrestling with build scripts, provisioning profiles, and signing certificates—instead of writing features. Fastlane streamlines every step of your iOS and Android release pipeline:
- Certificate & Profile management
- Automated builds (Xcode/Gradle)
- Code signing (match & keystores)
- Test execution (unit, UI)
- App Store / Play Store deployment

With a few configuration files and commands, Fastlane turns fragile manual processes into repeatable, secure, CI‑friendly lanes—so you can ship faster, with consistency and confidence.
Table of Contents
- Why Automate Builds & Signing
- Fastlane Setup & Initialization
- iOS Code Signing with match
- Defining iOS Lanes in the Fastfile
- Android Keystore Management
- Defining Android Lanes
- CI/CD Integration Examples
- Best Practices & Folder Structure
- Troubleshooting & Tips
- Conclusion
Why Automate Builds & Signing
| Challenge | Pain Point | Fastlane Solution |
|---|---|---|
| Manual certificate installs | Inconsistent profiles, developer-specific setups | match centralizes in Git repo |
| Complex Xcode settings | Frequent code signing errors | gym auto-configures signing |
| Gradle keystore configs | Hard to share secrets between team/CI | GPG‑encrypted keystore + env vars |
| Non-interactive CI requirements | Builds break without UI prompts | Fully scriptable lanes |
| Ad-hoc distribution | Manual TestFlight/App Store uploads | pilot and supply automate uploads |
Fastlane Setup & Initialization
Prerequisites
- Ruby ≥ 2.6 (rbenv, RVM, or system Ruby)
- Xcode CLI (
xcodebuild) for iOS - Android SDK & Java (with
sdkmanager,gradlein PATH)
Install Fastlane
bashCopyEdit# macOS (system Ruby)
sudo gem install fastlane -NV
# Linux (user install)
gem install fastlane --user-install
Tip: Pin Fastlane in a
Gemfilefor version consistency:rubyCopyEditsource 'https://rubygems.org' gem 'fastlane', '~> 2.250'
Initialize Fastlane
bashCopyEdit# iOS
cd ios
fastlane init
# Android
cd ../android # or your Android project root
fastlane init
Choose a template (beta App Store, Google Play, manual) or “Skip” to create a blank Fastfile.
iOS Code Signing with match
Fastlane’s match stores your certificates and provisioning profiles in a private Git repository, encrypted with a passphrase. All machines (dev & CI) pull the same assets.
Matchfile Configuration
rubyCopyEditgit_url("[email protected]:your_org/ios-certs.git")
type("appstore") # [development, appstore, adhoc, enterprise]
app_identifier(["com.example.app"]) # Your bundle IDs
username("[email protected]") # Apple ID with access
team_id("XXXXXXXXXX") # Apple Developer Team ID
readonly(false) # true for CI read-only access
Match Commands
| Command | Description |
|---|---|
fastlane match development | Generate & sync development certificates/profiles |
fastlane match appstore | Generate & sync App Store certificates/profiles |
fastlane match nuke | Revoke all certificates & profiles (use with care) |
Security: Store
MATCH_PASSWORDin your CI’s secrets to decrypt Git repo.
Defining iOS Lanes in the Fastfile
A lane is a sequence of Fastlane actions:

rubyCopyEditdefault_platform(:ios)
platform :ios do
before_all do
match(type: "development") # pull dev certs for tests
match(type: "appstore") # pull appstore certs for release
end
desc "Run tests and lint"
lane :ci do
scan(
scheme: "MyAppTests",
device: "iPhone 14",
clean: true,
code_coverage: true
)
swiftlint(strict: true)
end
desc "Build, archive, and upload to TestFlight"
lane :beta do
ci
gym(
workspace: "MyApp.xcworkspace",
scheme: "MyApp",
configuration: "Release",
output_directory: "build/ios",
output_name: "MyApp.ipa",
export_method: "app-store"
)
pilot(
skip_submission: true,
skip_waiting_for_build_processing: true
)
end
desc "Submit existing build for App Store review"
lane :release do
deliver(force: true)
end
end
scan: runs tests on simulatorgym: builds & archives.ipapilot: uploads to TestFlightdeliver: submits to App Store
Android Keystore Management
Android apps require a keystore for signing. Fastlane can decrypt an encrypted keystore, then invoke Gradle with the right properties.
Encrypt Your Keystore
bashCopyEdit# Interactive GPG symmetric encryption
gpg --symmetric --cipher-algo AES256 myapp.keystore
# Produces myapp.keystore.gpg
Store myapp.keystore.gpg in your repo, and add DECRYPT_PASSWORD to your CI secrets.
Matchfile for Android (optional)
Fastlane supports match for Android using cert store, but most teams use manual GPG encryption.
Defining Android Lanes
Edit android/fastlane/Fastfile:
rubyCopyEditdefault_platform(:android)
platform :android do
before_all do
# Decrypt keystore
sh "echo $DECRYPT_PASSWORD | gpg --batch --yes --passphrase-fd 0 -o keystore.jks -d myapp.keystore.gpg"
end
desc "Run unit tests and lint"
lane :ci do
gradle(task: "clean")
gradle(task: "testDebugUnitTest")
gradle(task: "lint")
end
desc "Build and deploy to Beta track on Google Play"
lane :beta do
ci
gradle(
task: "assembleRelease",
build_type: "Release",
properties: {
"android.injected.signing.store.file" => "keystore.jks",
"android.injected.signing.store.password" => ENV["KEYSTORE_PASSWORD"],
"android.injected.signing.key.alias" => ENV["KEY_ALIAS"],
"android.injected.signing.key.password" => ENV["KEY_PASSWORD"]
}
)
supply(
track: "beta",
json_key_data: ENV["PLAY_STORE_JSON_KEY"],
apk: "app/build/outputs/apk/release/app-release.apk"
)
end
desc "Promote Beta build to Production"
lane :release do
supply(
track: "production",
json_key_data: ENV["PLAY_STORE_JSON_KEY"],
apk: "app/build/outputs/apk/release/app-release.apk"
)
end
end
supply: uploads to Play Store- Properties: override Gradle signing config at runtime
CI/CD Integration Examples
GitHub Actions Workflow

yamlCopyEditname: Mobile CI/CD
on:
push:
branches: [ main ]
env:
# Common secrets names
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
jobs:
ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '2.7'
- name: Install Fastlane
run: gem install fastlane -NV
- name: iOS CI & Build
run: fastlane ios beta
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '2.7'
- name: Install Fastlane
run: gem install fastlane -NV
- name: Decrypt Keystore
run: |
echo $DECRYPT_PASSWORD | gpg --batch --yes --passphrase-fd 0 -o keystore.jks -d android/myapp.keystore.gpg
env:
DECRYPT_PASSWORD: ${{ secrets.DECRYPT_PASSWORD }}
- name: Android CI & Build
run: fastlane android beta
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_STORE_JSON_KEY }}
GitLab CI Example
yamlCopyEditstages:
- test
- build
iOS:
image: macos:latest
stage: build
before_script:
- gem install fastlane -NV
script:
- fastlane ios beta
only:
- main
Android:
image: ruby:2.7
stage: build
before_script:
- gem install fastlane -NV
- echo $DECRYPT_PASSWORD | gpg --batch --yes --passphrase-fd 0 -o keystore.jks -d android/myapp.keystore.gpg
script:
- fastlane android beta
only:
- main
Best Practices & Folder Structure
bashCopyEditproject-root/
├── ios/
│ └── fastlane/
│ ├── Fastfile
│ └── Matchfile
├── android/
│ └── fastlane/
│ └── Fastfile
├── Gemfile # pin fastlane version
├── myapp.keystore.gpg
└── README.md # FASTLANE.md with lane docs
- Gemfile: lock Fastlane, other Ruby gems.
- FASTLANE.md: document each lane, required env vars, how to run locally.
- Encrypted Assets: keysteores & certs under version control only as encrypted files.
- Clear Lane Naming:
ios ci,ios beta,android ci,android beta,android release.

Troubleshooting & Tips
| Symptom | Cause & Solution |
|---|---|
| Fastlane match fails with Git error | Ensure MATCH_PASSWORD is correct and SSH keys/cert access is set up. |
| Xcode code signing errors | Verify app_identifier, team_id, and provisioning profile matches. |
| Gradle signing not applied | Use android.injected.signing.* properties or configure build.gradle. |
| CI build hanging on prompt | Pass --readonly to match or set FASTLANE_DISABLE_COLORS=1. |
| Pilot upload times out | Use skip_waiting_for_build_processing: true to proceed. |
| Supply upload auth error | Ensure PLAY_STORE_JSON_KEY is valid JSON, no newlines lost in CI v |
Conclusion
Fastlane transforms complex mobile build and signing processes into simple, automated lanes. By adopting:
- match for centralized iOS certificate & profile management
- GPG‑encrypted keystores for Android in Git
- Gym/Gradle actions for reproducible builds
- Pilot/Supply for upload automation
- CI integration in GitHub Actions or GitLab
you’ll eliminate manual errors, empower continuous delivery, and let your team focus on code rather than provisioning. Start by running fastlane init, configuring your Fastfile, and automating one lane at a time—then watch your mobile release cycle shrink from hours to minutes. Happy shipping!























































































































































































































































































































































































































































































































































































































































































