Flutter CI/CD for App Store & Play Store (GitHub Actions + Fastlane)
Manual releases do not scale.
The third time you forget to bump the build number, upload the wrong provisioning profile, or ship a debug Firebase config to production, you start looking for automation.
CI/CD for Flutter mobile apps is harder than for a web deploy — you are dealing with code signing, macOS runners for iOS, keystore management, and two separate store consoles with different rules. But once the pipeline is in place, every release becomes repeatable instead of a Friday-night ritual.
This is an overview of how I set up Flutter CI/CD for production apps using GitHub Actions and Fastlane — not a copy-paste tutorial, but the decisions and pitfalls that matter when you are actually shipping to the App Store and Google Play.
CI/CD does not replace release judgment.
It removes repeatable mistakes so you can focus on store compliance and rollout strategy.
Why Flutter CI/CD Is Worth the Setup Cost
Without automation, release day typically involves:
- A developer with the only Mac that can archive iOS builds
- Manually editing version numbers in
pubspec.yaml - Uploading AAB/IPA files through browser consoles
- Hoping nobody committed secrets or debug flags
With a solid pipeline:
- Every merge to
mainproduces a verified release build - TestFlight and internal Play tracks update automatically
- Version bumps are consistent and traceable in git
- Signing credentials live in secure storage, not someone's laptop
Apps like Die Friedliche Geburt and Joii needed reliable release cadence across iOS and Android — wellness and mental health products where a broken release blocks users from content they depend on. Automation is how you keep that cadence without burning out the team.
The Stack: GitHub Actions + Fastlane
There are other options (Codemagic, Bitrise, self-hosted runners). GitHub Actions + Fastlane is what I use most often because:
- GitHub Actions — already where the code lives; good enough for most Flutter teams
- Fastlane — mature mobile release tooling for both iOS and Android with a large plugin ecosystem
You do not need both on day one. A reasonable progression:
- CI only — lint, test, and build APK/AAB on every PR
- CD to internal tracks — auto-deploy to TestFlight internal / Play internal testing
- CD to production — manual approval gate, then promote to App Store / Play production
Planning to build a Flutter app?
I help startups design and build scalable mobile apps with clean architecture, Firebase, and store-ready delivery.
Pipeline Stages That Actually Matter
Stage 1: Pull request checks
Run on every PR — fast feedback, no signing required:
# Conceptual steps — adapt to your repo
- flutter pub get
- flutter analyze
- flutter test
- flutter build apk --release # Android-only PR validation
Keep PR pipelines under 10–15 minutes where possible. Developers ignore slow CI.
Stage 2: Release build on merge
Triggered on merge to release/* or main (your branching strategy may vary):
flutter build appbundlefor Androidflutter build ipafor iOS (requires macOS runner)- Upload artifacts to GitHub Actions storage for manual inspection if needed
Stage 3: Store deployment via Fastlane
Fastlane lanes handle the store-specific work:
Android (supply):
- Upload AAB to Play Console
- Target a track:
internal→closed→production - Attach release notes from git tag or CHANGELOG
iOS (deliver / pilot):
- Upload IPA to App Store Connect
- Distribute to TestFlight testers
- Optionally submit for review (I usually keep review submission manual)
Code Signing: Where Most Pipelines Break
Signing is the hard part. Everything else is YAML.
Android
- Store your upload keystore as a base64-encoded secret in GitHub Actions (or use Google Play App Signing and keep the upload key secure)
- Write
key.propertiesand the keystore file in the CI job from secrets — never commit them - Use Play App Signing so Google holds the app signing key; you only manage the upload key
iOS
- Export certificates and provisioning profiles as encrypted secrets, or use Fastlane Match to sync signing assets from a private git repo
- macOS runners (
macos-latest) are required forflutter build ipa - Apple Developer account credentials: use App Store Connect API keys (preferred) instead of Apple ID passwords with 2FA gymnastics
If your CI pipeline only builds Android, you still have half a release process.
Budget for macOS runner minutes and iOS signing setup from the start.
A Minimal Fastlane Structure
Typical layout in a Flutter repo:
android/fastlane/Fastfile
android/fastlane/Appfile
ios/fastlane/Fastfile
ios/fastlane/Appfile
Example Android lane concept:
lane :internal do
upload_to_play_store(
track: 'internal',
aab: '../build/app/outputs/bundle/release/app-release.aab',
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true
)
end
Example iOS lane concept:
lane :beta do
build_app(
workspace: "Runner.xcworkspace",
scheme: "Runner"
)
upload_to_testflight(
skip_waiting_for_build_processing: true
)
end
Fastlane handles the store API calls; GitHub Actions orchestrates when lanes run.
GitHub Actions Workflow Patterns
Separate workflows for platform vs. combined
Option A — one workflow, matrix strategy:
strategy:
matrix:
platform: [android, ios]
Option B — separate workflow files:
.github/workflows/android-release.yml.github/workflows/ios-release.yml
I prefer separate files for mobile. iOS jobs need macOS runners (more expensive, slower) and fail for different reasons than Android. Splitting keeps debugging saner.
Secrets you will need
| Secret | Platform | Purpose |
|---|---|---|
ANDROID_KEYSTORE_BASE64 | Android | Upload keystore |
ANDROID_KEY_ALIAS / passwords | Android | Signing |
PLAY_STORE_JSON_KEY | Android | Play Console API |
MATCH_PASSWORD / certs repo | iOS | Code signing via Match |
APP_STORE_CONNECT_API_KEY | iOS | TestFlight / App Store upload |
Store these in GitHub Environment secrets with protection rules for production deployments.
Environment Flavors and CI
Production CI/CD assumes you have flavors or --dart-define flags separating dev, staging, and production Firebase projects.
Checklist for CI environments:
- Production builds use production Firebase config — not dev
- API base URLs injected at build time, not hardcoded
- Analytics and crash reporting keys match the target environment
- App name / bundle ID suffix distinguishes staging from production on device
If flavors are new to your project, nail them before automating releases. Automating the wrong config is worse than manual uploads.
For Firebase production readiness beyond CI, see Flutter + Firebase Production Checklist.
Release Gates: Do Not Auto-Ship to Production on Day One
Automating TestFlight and Play internal tracks is low risk. Automating production rollout on every merge is how you ship a regression to 100% of users at 2 AM.
Recommended gates:
- Internal track auto-deploy on merge to release branch
- QA sign-off on internal build (manual checklist)
- Manual workflow dispatch or approval environment for production
- Staged rollout on Play Store (start at 10%, monitor crash rates)
- Phased release on App Store (optional but useful for large user bases)
The App Store + Play Store Release Checklist covers privacy manifests, data safety forms, and review requirements that CI cannot validate — those stay human checks before production promotion.
Common Failures and Fixes
| Problem | Likely cause | Fix |
|---|---|---|
| iOS build fails on CI only | Provisioning profile mismatch | Match certs; verify bundle ID |
| Android upload rejected | Version code not incremented | Automate version bump in CI |
| Firebase wrong project in release | Missing --dart-define or flavor flag | Add env check step before build |
| TestFlight upload hangs | Apple processing delay | skip_waiting_for_build_processing: true |
| Play Store rejects AAB | Target SDK too old | Update compileSdk / target API in CI |
| CI secrets leaked in logs | Echoing signing files | Mask secrets; audit workflow output |
What CI/CD Does Not Replace
Automation handles build and upload. It does not handle:
- Store listing copy and screenshots — still manual in App Store Connect / Play Console (unless you automate metadata via Fastlane, which adds complexity)
- Privacy nutrition labels and data safety forms — human accuracy required
- Review rejection triage — Apple and Google still reject apps for policy reasons CI cannot predict
- Rollback decisions — you need monitoring (Firebase Crashlytics, Sentry) and a human call to halt rollout
For architecture that survives frequent releases, see Best Architecture for Scalable Flutter Apps.
When to Invest in CI/CD
Set up CI (lint + test + build) from week one. It is cheap and catches regressions early.
Add CD to internal tracks before your first beta user. Manual TestFlight uploads get old fast.
Add production CD only when you have monitoring, a rollback plan, and enough release history to trust the pipeline.
If you are hiring a Flutter freelancer to ship your MVP, ask whether they will leave you with a working pipeline — not just an app that only builds on their machine. That handoff detail belongs in every freelancer evaluation.
Need Help Setting Up Release Automation?
I set up Flutter CI/CD pipelines as part of production engagements — GitHub Actions, Fastlane, signing, and internal track deployment — so teams can ship confidently after handoff.
Review Flutter app development services and case studies for the kind of production work I take on. If you want help designing a release pipeline for your app, book a free consultation or send a message.
Have an app idea?
Let's build something scalable together — from MVP through store release.
See case studies for production examples.