Flutter CI/CD in 2026: Codemagic vs Fastlane vs GitHub Actions
Almost everything written about Flutter CI/CD is written by someone selling Flutter CI/CD. That is not a conspiracy, it is just who has the budget to publish. The result is a genre of article where every tool wins its own comparison, and the one question you actually have — which of these should I put my team on — never gets answered.
At iMumz I ship across four client applications and I have run all three of these in production: Codemagic, Fastlane, and GitHub Actions, against Play Console, App Store Connect, and TestFlight. I have also done 250+ PR reviews, which is relevant here in a way that is not obvious: CI is mostly a code review problem wearing a DevOps costume. What your pipeline runs on every pull request determines what your reviewers no longer have to think about.
So this is the comparison as I would give it to a colleague. No benchmarks — I do not have published build-time numbers for these tools and I am not going to invent any, because build times depend on your dependency graph, your cache hit rate, and the machine class you paid for, which means any number I quoted would be about my app rather than yours. The argument here is architectural, and architecture is the part that transfers.
These are not three options
The framing error underneath most of these comparisons is treating the three as competitors. They are not on the same layer.
Fastlane is a tool for doing the release work. It talks to App Store Connect, it manages certificates, it uploads builds, it bumps versions. It does not decide when to run.
GitHub Actions is a tool for deciding when to run things and giving you a machine to run them on. It knows nothing about iOS. Anything it does with a provisioning profile, it does because you told it how — often by calling Fastlane.
Codemagic is a tool for deciding when to run things, giving you a machine, and already knowing about iOS. It is the same layer as GitHub Actions plus opinions about mobile.
Which means the real question is not "which of the three." It is: do I want to own the mobile-specific knowledge in my pipeline, or rent it? Fastlane comes along either way in most setups — it is the layer underneath, and it does not go anywhere.
Fastlane: the layer underneath
Fastlane is the oldest thing here and the one people underrate, usually because Ruby is not fashionable and the DSL looks dated.
What it is genuinely good at: the long tail of Apple and Google's release APIs. Uploading to TestFlight, submitting to review, managing App Store Connect API keys, pushing an AAB to a Play Console track with a staged rollout percentage, syncing certificates across a team with match. These are not hard problems conceptually, but they are enormous in surface area, and every one of them has a special case that someone already hit and fixed for you.
What it is not: a scheduler. Fastlane has no opinion about when your lane runs. It happily runs on a developer's laptop, which is both its best and worst property — best because you can debug it locally instead of through a fifteen-minute CI feedback loop, worst because a lane that only ever runs on a laptop is a release process with a single point of failure wearing a hoodie.
The practical position: write your release logic as Fastlane lanes regardless of which runner you pick. That is the portability argument, and it is the strongest reason to use it. Your lane is the same lane whether it is invoked by GitHub Actions, by Codemagic, or by you at 11pm because the pipeline is down and the build has to go out. Runners are something you switch every few years. Release logic is something you keep.
# Fastfile — the logic that survives a runner migration
platform :ios do
lane :beta do
setup_ci if ENV['CI'] # keychain handling on ephemeral machines
sync_code_signing(type: 'appstore', readonly: true)
build_app(scheme: 'Runner', export_method: 'app-store')
upload_to_testflight(skip_waiting_for_build_processing: true)
end
end
Note setup_ci. That single line is Fastlane knowing that CI machines have a keychain problem, and it is a decent proxy for the whole value proposition: a hundred small pieces of institutional knowledge about Apple's toolchain that you would otherwise acquire one outage at a time.
GitHub Actions: the runner you already have
The case for GitHub Actions is mostly gravity. Your code is already there. Your pull requests are already there. Your secrets store is already there. Your team already reads the checks UI. Adding a second product to the loop has a cost that nobody puts on the invoice.
And for the part of Flutter CI that is not mobile-specific, it is genuinely excellent. Analyze, format-check, unit tests, coverage upload — this is a Linux container running flutter test, and there is nothing about it that wants a specialist tool. If your pipeline is mostly gates rather than releases, GitHub Actions is not a compromise, it is the correct answer.
name: pr-gates
on: pull_request
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { channel: stable, cache: true }
- run: flutter pub get
- run: flutter analyze
- run: flutter test --coverage
Where it gets expensive is iOS. macOS runners are billed at a multiple of Linux minutes, and that multiplier is the single most important line in the whole comparison — it is why a pipeline that felt free on Linux suddenly has a budget conversation attached the moment you add an iOS build. Every iOS job you run on GitHub-hosted macOS is a decision, not a default.
The other cost is that you own the mobile knowledge. Keychain setup, provisioning profile installation, the flutter build ipa incantations, caching CocoaPods correctly across runs. All of it is solvable — usually by calling Fastlane, which is why the two pair so naturally — but "solvable" means someone on your team is now the person who knows why the base64-encoded p12 has to be decoded into a temporary keychain that gets unlocked before the build step. That person is a bus factor.
Codemagic: the managed answer
Codemagic's pitch is that it already knows all of that. It is mobile-first in a way GitHub Actions is deliberately not: it understands Flutter as a first-class project type, it has code signing as a UI concept rather than a shell script, and it integrates with App Store Connect and Play Console as features rather than as things you script.
What that buys you, concretely: the iOS signing setup goes from a day of debugging to an upload and a dropdown. Build machine images come with Xcode, CocoaPods, and the Flutter SDK already installed and version-pinned. Publishing to TestFlight is configuration, not code. And when Apple changes something — which they do — the fix arrives on their side rather than yours.
What it costs you, beyond money: a second system. Your CI config now lives in codemagic.yaml alongside whatever is in .github/workflows, your secrets live in two places, and a new engineer has to learn two products. Teams routinely underestimate this. The organisational cost of a split pipeline is real even when the technical case for it is sound.
My honest read: Codemagic earns its price when you are shipping iOS regularly and nobody on the team wants to own the Apple toolchain. That is not a rare situation — it describes most Flutter teams, because most Flutter teams chose Flutter partly to avoid becoming iOS specialists. Paying to keep that promise is a coherent decision, not a cop-out.
Code signing is the actual problem
Every honest conversation about mobile CI is a conversation about code signing wearing a different hat. It is where the days go.
The reason is structural. Signing is stateful in a world that wants to be stateless. A CI runner is ephemeral by design — it comes up clean, does work, and dies. Apple's model assumes a durable machine with a keychain, a set of installed certificates, and provisioning profiles that match your bundle identifier, your capabilities, and your device list. You are reconstructing a stateful workstation from scratch on every build, and every step of the reconstruction can fail in a way whose error message is a lie.
Android is meaningfully easier — a keystore file and a password, decoded into place — but it has its own trap, which is that the keystore is unrecoverable. Lose it and you cannot update the app under that key, which is why Play App Signing exists and why you should use it. That is one of the few genuinely one-way doors in mobile releases.
The three approaches, plainly:
- Fastlane
match: certificates and profiles in an encrypted git repo, synced to whatever machine needs them. Explicit, portable, works everywhere, and requires you to understand what it is doing. The infrastructure-as-code answer. - Manual secrets in GitHub Actions: base64 your p12 and profile into repository secrets, decode them into a temporary keychain at build time. Free, entirely yours, and the source of more wasted afternoons than anything else in this article.
- Codemagic's managed signing: upload once, reference by name. This is the specific thing you are paying for, and if signing is your pain, it is most of the value right there.
The rule I would give: if code signing is costing your team days per quarter, that is the number to compare against a subscription — not build minutes. Engineer time debugging a keychain is the most expensive thing in the pipeline and it never appears in the cost model.
The decision table
| Dimension | Fastlane | GitHub Actions | Codemagic |
|---|---|---|---|
| Layer | Release automation (runs anywhere) | Generic CI runner | Mobile-specific CI runner |
| Knows what Flutter is | Via plugins; not natively | No — you configure it | Yes, first-class |
| iOS code signing | match: explicit and portable | You script it (usually via Fastlane) | Managed — upload and reference |
| Runs locally for debugging | Yes — its best property | Only approximately (act) | No |
| Cost model | Free, open source | Free tier; macOS billed at a multiple of Linux | Paid tiers plus a free allowance; macOS priced as a mobile product |
| Where the pipeline lives | Fastfile in your repo | .github/workflows, next to the code | codemagic.yaml or their UI — a second system |
| Store publishing | Deep — TestFlight, Play tracks, staged rollout | Only what you script | Built-in as configuration |
| Who owns Apple's breakage | You (but the community patches fast) | You | Them |
| Onboarding cost for a new engineer | Ruby DSL to read | Already known if they know GitHub | A new product to learn |
| Best fit | Everyone — as the layer underneath | PR gates, Android releases, teams with CI skill in-house | iOS-heavy shipping where nobody wants to own Xcode |
The cost model nobody reads carefully
The comparison people make is subscription price versus free tier, and it is the wrong comparison twice over.
First, GitHub Actions is not free for mobile. The Linux minutes are effectively free at small-team scale; the macOS minutes are not, and iOS builds need macOS. A team that "saved money" by staying on GitHub Actions and then runs an iOS build on every push to a feature branch is not saving money — they are just paying it to a different vendor under a line item nobody reviews.
Second, the real cost is engineer time. A day of debugging a signing failure costs more than a month of most CI subscriptions, and the failure recurs whenever Apple changes something. That is the number to model, and it is the number nobody models because it is not on a bill.
The trade-off is the standard managed-versus-self-hosted one and it resolves the standard way: rent the thing you do not want to become good at. If your team wants CI expertise in-house — because you are running self-hosted macOS hardware, because you have compliance constraints on where builds happen, because you have someone who genuinely enjoys this — own it. If your team wants to ship a Flutter app, rent it.
What I would actually set up
For most Flutter teams, the split I would defend:
- GitHub Actions for every pull request. Analyze, format, unit and widget tests, coverage. Linux, fast, free, and it runs on every PR without anyone thinking about it. This is the highest-value CI you will ever configure, and it is the cheapest — it is why the testing strategy that reaches 85% coverage is a CI topic and not just a testing topic. A test suite nobody is forced to run is a documentation format.
- Fastlane lanes for all release logic. Version bumps, builds, uploads, store metadata. In the repo, runnable locally, invoked by whatever runner you end up with.
- Release builds wherever signing hurts least. Android on GitHub Actions is fine — a keystore and a password. iOS is where the choice bites, and I would not fight it on principle. If your team is losing days to signing, Codemagic is the correct spend.
- Gate on the things that catch real regressions. Analyze as an error, not a warning. Tests required to merge. Across 250+ PR reviews the pattern was consistent: every check I could hand to CI was a check I stopped spending human attention on, and human attention is better spent on whether the abstraction is right than on whether someone forgot a
mountedguard — the class of bug behind a lot of the crash work I did at iMumz.
Running four client applications makes one more thing obvious that a single-app team never notices: your pipeline should be one thing used four times, not four things. Four apps with four bespoke workflows means four places to fix the same Apple change. Shared lanes, shared workflow templates, per-app configuration. The same instinct as any other code you would refuse to copy-paste.
The Takeaway
Codemagic, Fastlane, and GitHub Actions are not three competing choices — Fastlane is the release layer underneath, and the real decision is whether you rent mobile-specific CI knowledge or own it. Use GitHub Actions for pull request gates and Android releases, because that part of the pipeline has nothing mobile-specific about it and the gravity of living next to your code is real. Write release logic as Fastlane lanes regardless, because runners change and release logic does not. Codemagic earns its price when iOS code signing is costing your team days per quarter, which is the number to compare against a subscription — not build minutes. And the highest-value pipeline you will ever configure is still the boring one that runs your tests on every PR.
I'm a mobile engineer (Flutter, React Native, native Android) currently open to full-time roles — see my résumé or what I've shipped. The production app behind most of this is the iMumz case study.
Frequently asked questions
Should I use Codemagic or GitHub Actions for Flutter CI/CD?
GitHub Actions for pull request gates and Android releases — that work has nothing mobile-specific about it and living next to your code is worth a lot. Codemagic earns its price when you ship iOS regularly and nobody on the team wants to own the Apple toolchain.
Is Fastlane still relevant if I use Codemagic or GitHub Actions?
Yes, because it is a different layer. Fastlane does the release work; the other two decide when it runs. Writing release logic as Fastlane lanes keeps it portable, runnable locally, and independent of whichever runner you use this year.
Why is iOS code signing so hard in CI?
Signing is stateful and CI runners are ephemeral by design, so you rebuild a workstation's keychain, certificates, and provisioning profiles on every build. Fastlane match, manual base64 secrets, and managed signing are the three answers — and each failure mode has an error message that lies.
Is GitHub Actions actually free for Flutter CI?
For Linux jobs at small-team scale, effectively yes. iOS builds need macOS runners, which bill at a multiple of Linux minutes — so a pipeline that felt free acquires a budget conversation the moment you add iOS.
What is the highest-value Flutter pipeline to set up first?
Analyze, format-check, and tests on every pull request, on a Linux runner. It is the cheapest CI you will ever configure and it removes an entire category of review comment.
Bottom line
Codemagic, Fastlane, and GitHub Actions are not three options — Fastlane is the layer underneath, and the real question is whether you rent mobile CI knowledge or own it. Compare a subscription against the days per quarter you lose to code signing, not against build minutes.
More from the field.
Like the way I think?
I'm a mobile engineer — Flutter and React Native for cross-platform, native Android in Kotlin where the platform demands it, Next.js on the web — open to full-time roles. If your team is hiring, the résumé is the fastest way in.