Journal

GitHub Actions + Fastlane for React Native iOS in 2026

Five jobs, identically, every time. The 2026 question is architecture, not tooling: managed service or your own lanes.

GitHub Actions + Fastlane for React Native iOS in 2026: the App Store logo on a glass tile over a blue gradient with bubbles

TL;DR

React Native iOS CI in 2026 forks on ownership: EAS Build for Expo apps that want managed signing and zero maintenance, GitHub Actions plus fastlane when you need custom steps, monorepos, or the whole machine. The self-managed core is one beta lane, setup_ci, readonly match from an encrypted signing repo, gym, pilot, authenticated by an App Store Connect API key instead of a human Apple ID. Cost discipline matters at $0.062 per macOS minute: lockfile-keyed caches for JS deps and Pods, lint and tests on Linux, builds on tags rather than every push, Xcode version pinned. A free VP0 design covers the app screens; this pipeline turns them into a TestFlight link on every merge.

What does the pipeline actually have to do?

Five jobs, every time, identically: install JS dependencies, install pods, sign the build with certificates no human touched, produce the .ipa, and push it to TestFlight. Everything else (screenshots, release notes, version bumps) is decoration on those five. fastlane (the repo carries 41,620 stars) remains the orchestration layer the iOS world standardized on, and GitHub Actions supplies the macOS machines, which leaves the 2026 question as architecture, not tooling: managed build service or self-managed lanes?

RouteWhere signing livesCost shapeMost useful for
GitHub Actions + fastlaneYour match repo, your lanesmacOS minutes at $0.062/minFull control, custom steps, monorepos
EAS BuildExpo’s managed signingPlan-basedExpo apps that want zero CI ownership
Xcode CloudApple-managed25 free compute hours monthly, then tiersPure-native teams living in Xcode

For Expo-managed apps, EAS is the path of least resistance and there is no shame in taking it. The GitHub Actions route earns its keep when you need what managed services meter or forbid: custom native steps, self-hosted runners, monorepo orchestration, or simply owning the whole machine.

How does signing work with nobody at the keyboard?

Two pieces, both fastlane’s. match stores certificates and provisioning profiles encrypted in a private git repo; CI clones it read-only (match(type: "appstore", readonly: true)), decrypts with a passphrase from secrets, and installs into a temporary keychain. No certificate lives in the CI config, rotation means re-running match once, and every machine, laptop or runner, signs identically.

App Store Connect API keys replace the Apple-ID-with-2FA bot account that used to make CI authentication miserable: an API key (key ID, issuer ID, .p8 content) stored as repository secrets authenticates both match’s profile management and the TestFlight upload. If your pipeline still logs in as a human Apple ID, this is the single upgrade to make first.

The lane that does it all stays small:

lane :beta do
  setup_ci
  match(type: "appstore", readonly: true)
  gym(scheme: "MyApp", export_method: "app-store")
  pilot(skip_waiting_for_build_processing: true)
end

setup_ci creates the temporary keychain; gym builds and archives; pilot ships to TestFlight without burning paid minutes waiting for Apple’s processing.

Where do the macOS minutes actually go?

Mostly into work you can cache away. At $0.062 per minute, a 35-minute uncached build costs real money across a team’s daily pushes, and three caches reclaim most of it: the JS dependency store (keyed on the lockfile), CocoaPods (Pods/ keyed on Podfile.lock), and, with more care, DerivedData. A typical RN app drops from ~35 to ~15 minutes with the first two alone; cache hit rate is the metric worth watching in a pipeline you pay for by the minute.

Two structural choices compound the savings. Split lint-and-test jobs onto Linux runners, since nothing about ESLint or Jest needs a Mac, and reserve the macOS runner for the build lane alone. And trigger deliberately: TestFlight on tags or a release branch rather than every push to main, with PR builds running tests only. The workflow file ends up shaped like the platform docs’ standard matrix, checkout, Node with cache, pods restore, fastlane beta, with the secrets block (match passphrase, ASC key trio) as the only project-specific part.

What breaks first, and how do you see it?

CI failures in this stack cluster in three places. Signing drift: a renewed certificate or an added device invalidates profiles, and the fix is re-running match outside CI, never hand-editing in the portal, which un-syncs the repo that was supposed to be truth. Xcode version skew: runner images update on GitHub’s schedule, so pin the Xcode version in the workflow and treat image updates as deliberate upgrades. And the silent JS-native mismatch after dependency bumps, the same triage family as local debugging, where the discipline of verifying what actually ran, covered from the network side in the empty-network-tab triage, applies to build logs verbatim.

The pipeline also changes what release discipline costs elsewhere: a repeatable train is what makes raising a force-update floor a checkbox instead of a gamble, and the fastlane lanes extend naturally into the screenshot automation once builds are boring. For the app itself, screens are the part an agent generates well: a free VP0 design gives Claude Code or Cursor the structure, and the pipeline here is what turns those generated screens into a TestFlight link on every merge.

The other half of a lean pipeline, cutting the bundle the agent bloated, is covered in React Native bundle-size optimization.

Key takeaways: RN iOS CI in 2026

  • EAS for Expo apps wanting zero CI ownership; Actions + fastlane when you need the machine.
  • match + an App Store Connect API key is the whole signing story: encrypted repo, read-only in CI, no human Apple ID.
  • Cache the lockfile-keyed stores (JS deps, Pods) and watch the hit rate; macOS minutes bill at $0.062 each.
  • Lint and test on Linux; build on macOS; ship on tags, not on every push.
  • Pin the Xcode version and treat runner-image updates as scheduled upgrades, not surprises.

Frequently asked questions

How do I set up GitHub Actions with fastlane for React Native iOS in 2026? One beta lane: setup_ci for the temporary keychain, match in readonly mode pulling signing from an encrypted git repo, gym to archive, pilot to push TestFlight, authenticated end to end by an App Store Connect API key stored in repository secrets. Cache JS dependencies and Pods on their lockfiles, and keep lint and tests on Linux runners.

Is EAS Build better than GitHub Actions for Expo apps? For most Expo-managed apps, yes: managed signing and zero CI maintenance beat owning lanes. The Actions route wins when you need custom native steps, monorepo orchestration, self-hosted runners, or full control of the build machine.

How does code signing work in CI without a person logging in? fastlane match keeps certificates and profiles encrypted in a private repo that CI decrypts read-only into a temporary keychain, while an App Store Connect API key handles authentication. No 2FA prompts, no certificates pasted into CI settings, rotation in one command.

Why are my macOS CI builds so expensive? GitHub-hosted macOS runners bill at $0.062 per minute, several times the Linux rate, so uncached pod installs and JS installs dominate cost. Lockfile-keyed caches typically cut a 35-minute build to roughly 15, and moving tests to Linux keeps the Mac for the archive alone.

Should the pipeline deploy on every push? No: tests on every PR, TestFlight on tags or a release branch. Every-push deployment burns minutes, floods TestFlight with noise builds, and makes the build number a meaningless counter.

Questions from the community

How do I set up GitHub Actions with fastlane for React Native iOS?

One beta lane: setup_ci creates a temporary keychain, match in readonly mode installs signing from an encrypted git repo, gym archives, and pilot uploads to TestFlight, all authenticated by an App Store Connect API key in repository secrets. Cache JS dependencies and Pods on their lockfiles and keep lint and tests on cheaper Linux runners.

Is EAS Build better than GitHub Actions with fastlane?

For most Expo-managed apps, yes: managed signing and zero CI ownership win. Choose Actions plus fastlane when you need custom native build steps, monorepo orchestration, self-hosted runners, or full control over Xcode versions and the build machine.

How does iOS code signing work in CI without a human?

fastlane match stores certificates and provisioning profiles encrypted in a private repo; CI clones it read-only, decrypts with a passphrase from secrets, and installs into a temporary keychain, while an App Store Connect API key replaces the old 2FA bot-account login entirely.

Why are macOS CI minutes so expensive and how do I cut them?

GitHub-hosted macOS runners bill at $0.062 per minute, several times the Linux rate. Lockfile-keyed caches for node modules and CocoaPods typically cut a 35-minute uncached build to about 15, and moving lint and tests to Linux reserves the Mac for the archive alone.

Should CI deploy to TestFlight on every push?

No. Run tests on every PR and ship to TestFlight on tags or a release branch. Per-push deployment burns paid minutes, floods TestFlight with noise builds, and erodes the meaning of the build number.

Part of the React Native & Expo: Mobile Frontend Architecture hub. Browse all VP0 topics →

Keep reading

How to Update an Old React Native App Using AI: a glass iPhone UI wireframe icon on a holographic purple gradient
Workflows 6 min read

How to Update an Old React Native App Using AI

The agent is for the grind, not the judgment: incremental version-by-version upgrades, the Upgrade Helper diff as the map, and verify-after-each-step.

Lawrence Arya · June 7, 2026
Ionic to React Native: The AI Prompt That Works: a glass iPhone app-grid icon on a mint and teal gradient
Workflows 6 min read

Ionic to React Native: The AI Prompt That Works

No converter exists, and the prompt is a sequence: logic first, screens rebuilt natively one at a time, plugins mapped, with a hard ban on literal translation.

Lawrence Arya · June 7, 2026
Migrate from Expo Go to a Development Build with AI: a glass app tile showing the VP0 logo on a pink and blue gradient
Workflows 6 min read

Migrate from Expo Go to a Development Build with AI

A development build is your own Expo Go, not an exit from Expo: install expo-dev-client, audit native deps, stay managed, and the JS loop is unchanged.

Lawrence Arya · June 7, 2026
How to Fix React Native RTL Flexbox Layout (with AI): a glowing iPhone home-screen icon on a purple and blue gradient
Workflows 5 min read

How to Fix React Native RTL Flexbox Layout (with AI)

RTL layouts break in React Native when you hard-code left and right. Use logical start and end props, let rows flip, and prompt AI for RTL-safe styles.

Lawrence Arya · June 4, 2026
React Native Text Cut Off on iPhone SE: The Fix: a phone toggle icon surrounded by location, calendar, settings, wallet and chart app icons on a coral gradient
Workflows 5 min read

React Native Text Cut Off on iPhone SE: The Fix

Text clips on the iPhone SE because of fixed heights and text that cannot shrink or wrap. Here is why it happens and the React Native fixes that hold at any size.

Lawrence Arya · June 4, 2026
Bolt.new React Router Errors in Expo? Swap the Router: a vivid neon 3D App Store icon on an orange, pink and blue gradient
Workflows 5 min read

Bolt.new React Router Errors in Expo? Swap the Router

Bolt.new app throwing React Router DOM errors when you move to Expo mobile? React Router is for the web. Replace it with Expo Router or React Navigation.

Lawrence Arya · June 1, 2026