Mobile DevOps at Scale: Automating Flutter Releases with Fastlane & GitHub Actions
A complete guide to automating Flutter deployments. Set up Fastlane Match for code signing, GitHub Actions for cloud builds, and Firebase App Distribution for QA testing.

A complete guide to automating Flutter deployments. Set up Fastlane Match for code signing, GitHub Actions for cloud builds, and Firebase App Distribution for QA testing.
Mobile DevOps at Scale: Automating Flutter Releases with Fastlane & GitHub Actions
Scenario: It's Friday, 5 PM. Your PM asks: "Can we ship that hotfix to the App Store?" You sigh.
- 2.Open Xcode.
- 4.Increment Build Number.
- 6.Archive. (Wait 10 mins).
- 8.Upload. (Wait 15 mins).
- 10.Login to App Store Connect.
- 12.Select Build.
- 14.Submit for Review.
You just wasted 30 minutes of your life. And if you forgot to run tests? You just shipped a crash.
This ends today.
Over the last 5 years, I have built pipelines that turn that 30-minute manual hell into a single git command:
git tag v1.0.1 && git push --tags
The robot handles the rest.
In this deep dive, we will configure:
- 2.Fastlane for local automation.
- 4.Match for code signing (the nightmare of iOS dev).
- 6.GitHub Actions for cloud execution.
- 8.Firebase App Distribution for QA.
Part 1: Fastlane (The Automation Engine)
Fastlane is a set of Ruby scripts that wrap the ugly xcodebuild and gradle commands.
Installation:
bashbrew install fastlane cd android && fastlane init cd ios && fastlane init
iOS Configuration (ios/fastlane/Fastfile)
We define "Lanes". A lane is a workflow.
rubydefault_platform(:ios) platform :ios do desc "Push a new beta build to TestFlight" lane :beta do increment_build_number(xcodeproj: "Runner.xcodeproj") build_app(workspace: "Runner.xcworkspace", scheme: "Runner") upload_to_testflight end end
Now, running fastlane ios beta does everything.
Part 2: The Code Signing Nightmare (Fastlane Match)
The #1 reason CI/CD fails on iOS is Certificates and Provisioning Profiles. "Certificate not found in keychain". "Profile doesn't match bundle ID".
The Solution: Fastlane Match. It stores your certificates encrypted in a private Git repository.
- 2.Create a private repo:
my-company/certificates. - 4.Run
fastlane match init. - 6.Run
fastlane match appstore. - 8.Run
fastlane match development.
Now, your certificates live in cloud storage (Git). On your CI server (GitHub Actions), you just run "Match", pass the decryption password, and it installs the certs into the temporary keychain.
Zero manual keychain Access.
Part 3: Android Configuration (android/fastlane/Fastfile)
Android is easier (just a Keystore), but we still need to automate the Play Store upload.
first, perform json_key_file setup for Google Play Console API access.
rubyplatform :android do desc "Deploy to Play Store Internal Track" lane :internal do gradle(task: "bundle", build_type: "Release") upload_to_play_store(track: "internal", json_key: "play-store-creds.json") end end
Part 4: Managing Secrets (The .env Approach)
Never commit play-store-creds.json or passwords to Git.
Use Environment Variables.
In both Fastfiles:
rubyjson_key_file(ENV["GOOGLE_JSON_KEY_FILE"]) store_password(ENV["ANDROID_STORE_PASSWORD"]) key_password(ENV["ANDROID_KEY_PASSWORD"])
In GitHub Actions, we will inject these secrets.
Part 5: GitHub Actions (The Cloud Runner)
Now we move the execution from your laptop to the cloud.
Create .github/workflows/deploy.yml:
yamlname: Deploy to App Stores on: push: tags: - 'v*' # Trigger on version tags (v1.0.0) jobs: deploy_ios: runs-on: macos-latest # Expensive, but required for Xcode steps: - uses: actions/checkout@v4 - name: Setup Flutter uses: subosito/flutter-action@v2 with: channel: 'stable' - name: Install Dependencies run: flutter pub get - name: Decrypt Secrets run: echo "$GOOGLE_JSON_KEY" > android/key.json env: GOOGLE_JSON_KEY: ${{ secrets.GOOGLE_JSON_KEY }} - name: Fastlane Match (Install Certs) working-directory: ios run: fastlane match appstore --readonly env: MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} - name: Build & Deploy iOS working-directory: ios run: fastlane beta env: APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_API_KEY }} deploy_android: runs-on: ubuntu-latest # Cheaper/Faster than Mac steps: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 - name: Build & Deploy Android working-directory: android run: fastlane internal
Part 6: Firebase App Distribution (For QA)
TestFlight takes 20-30 minutes "processing". Play Store Internal takes 2-4 hours.
If you just want to show the app to your QA team, use Firebase App Distribution. It is instant.
Add a lane:
rubylane :qa do flutter_build_ipa firebase_app_distribution( app: "1:123456789:ios:xxxxxx", testers: "qa-team@company.com", release_notes: "New feature: Dark Mode" ) end
This sends an email to your QA team immediately with a "Download" button.
Part 7: Versioning Strategy
Don't manually edit pubspec.yaml version.
Automate it.
- 2.Use
cider(a Dart tool for manipulating pubspec). - 4.In CI, extract version from Git Tag.
- Tag:
v1.2.3-> Version:1.2.3. - Build Number:
github.run_number.
yaml- name: Update Version run: | cider version ${{ github.ref_name }} cider build ${{ github.run_number }}
Conclusion: The ROI of DevOps
Setting this up takes 2 full days. It is painful. You will fight with Ruby versions. You will fight with Apple Permissions.
But once it works? You save 4 hours per release. If you release weekly, that is 200 hours a year. That is 5 weeks of vacation.
Do not be the developer who manually archives builds. Be the engineer who pushes a tag and goes to get coffee.
Resources
About the Author: Sachin Sharma is a Mobile DevOps expert. He has managed CI/CD pipelines for apps with 10M+ downloads and believes that manual releases are a bug.

Mastering tRPC: End-to-End Type Safety Without GraphQL
REST is loose. GraphQL is verbose. tRPC is the future. In this 4,000-word guide, we build a monorepo where changing a database column instantly breaks the frontend build.

System Design: Architecting a Real-Time Collaboration Engine (Like Figma)
Searching for 'How to build Figma' only gives you 'Use Socket.io'. This 4,800-word guide goes deeper. We implement CRDTs (Yjs/Automerge), WebSocket scaling strategies, and handle the 'CAP Theorem' in production.