Automate Flutter iOS/Android Builds, Code Signing, and App Store Deployments with Fastlane + GitHub Actions

Developing apps with Flutter is a joy, but the process of deploying the finished app to the App Store and Google Play Store often causes significant stress for developers. Managing certificates, matching provisioning profiles, bumping build versions, and uploading to the market require far too many manual steps.
This guide covers how to combine Fastlane and GitHub Actions to fully automate the iOS and Android builds, code signing, and store deployment processes for your Flutter app.
Why Do We Need CI/CD Automation?
The following issues frequently occur during the manual deployment process:
- Human Error: Mistakes like mismatching certificates or forgetting to bump the build version.
- Wasted Time: Time spent waiting while archiving on a local machine and uploading to the store.
- Dependency: The deployment process relies on a specific developer’s local environment (especially Mac) and accounts.
By using GitHub Actions and Fastlane, you can securely handle all these processes in the cloud simply by pushing code to the main or release branch.
1. Fastlane Setup (Local)
First, you need to initialize Fastlane in the android and ios directories of your Flutter project, respectively.
Android Fastlane Initialization
cd android
fastlane init
During the initialization process, it will ask for the package name and the path to the JSON secret key. You need the Service Account JSON key issued from the Google Play Console.
Example android/fastlane/Fastfile:
default_platform(:android)
platform :android do
desc "Submit a new Beta Build to Crashlytics"
lane :beta do
gradle(task: "clean bundleRelease")
upload_to_play_store(track: 'beta')
end
end
iOS Fastlane Initialization
For iOS deployment, managing certificates and provisioning profiles is crucial. Using Fastlane’s match allows you to securely share this process with team members via a Git repository and easily load it in CI/CD.
cd ios
fastlane init
fastlane match init
Example ios/fastlane/Fastfile:
default_platform(:ios)
platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
setup_ci
match(type: "appstore", readonly: true)
# Since Flutter builds are done in GitHub Actions, Fastlane handles the already built app.
build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
upload_to_testflight
end
end
[!IMPORTANT] To execute iOS code signing in a CI environment, using Fastlane Match is highly recommended. It is much safer and easier to manage than exporting certificates locally and uploading them to CI.
2. Configuring the GitHub Actions Workflow
Now, create a .github/workflows/deploy.yml file at the root of the repository.
Basic Workflow Structure and Environment Variables
You must save the following values in GitHub Secrets in advance:
MATCH_PASSWORD: Fastlane Match decryption passwordMATCH_GIT_BASIC_AUTHORIZATION: PAT (Personal Access Token) for Match repository accessAPP_STORE_CONNECT_API_KEY_KEY_ID: App Store Connect API Key IDAPP_STORE_CONNECT_API_KEY_ISSUER_ID: App Store Connect API Issuer IDAPP_STORE_CONNECT_API_KEY_KEY: App Store Connect API Key (.p8 contents)PLAY_STORE_CONFIG_JSON: Google Play Service Account JSON contents
Full Workflow Script
name: Deploy to App Store & Google Play
on:
push:
tags:
- 'v*' # Runs when pushing tags like v1.0.0
jobs:
build-and-deploy:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '17'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Install dependencies
run: flutter pub get
# --- iOS Deployment Pipeline ---
- name: Setup Ruby for Fastlane
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: ios
- name: Decode App Store Connect API Key
run: |
mkdir -p ~/.appstoreconnect/private_keys/
echo "${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}" > ~/.appstoreconnect/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }}.p8
- name: Build iOS App
run: flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
- name: Deploy iOS to TestFlight
working-directory: ios
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
run: bundle exec fastlane beta
# --- Android Deployment Pipeline ---
- name: Setup Ruby for Fastlane (Android)
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: android
- name: Decode Play Store JSON
run: echo "${{ secrets.PLAY_STORE_CONFIG_JSON }}" > android/play_store_config.json
- name: Build Android App
run: flutter build appbundle --release
- name: Deploy Android to Play Store
working-directory: android
run: bundle exec fastlane beta
Key Considerations
- Mac Environment Selection: You must use the
macos-latestrunner for iOS builds. (Note that billing rates are higher than Linux runners.) - Flutter Build Optimization: For iOS, the
flutter build ipacommand can handle everything up to archiving. In this case, configuring Fastlane to only handle the upload role is advantageous for speed. - API Key Authentication: The traditional Apple ID/password method is very tricky to use in CI environments due to 2FA. Be sure to issue and use an App Store Connect API Key.
Troubleshooting Guide
| Symptom | Cause and Solution |
|---|---|
Missing private key for ... (iOS) |
Unable to access the Fastlane Match repository, or the MATCH_PASSWORD is incorrect. Check the Secret values. |
Google Api Error: Invalid request - Invalid package name (Android) |
Occurs when the app has never been registered in the Google Play Console. The very first deployment must be uploaded manually as an AAB. |
Code signing is required for product type 'Application' (iOS) |
The signing setting in the Xcode project file (.pbxproj) is set to ‘Automatic’, and the provisioning profile is not injected. Change the project settings to use Match. |
Conclusion
Building a CI/CD pipeline may take half a day for initial setup, but once established, it saves a tremendous amount of time and stress over dozens or hundreds of subsequent deployments. Especially in team development, it positively transforms the development culture by eliminating the worry of ‘who will deploy.’
Introduce Fastlane and GitHub Actions to your project right now and create an environment where you can focus solely on development.