Why We Stopped Managing iOS Certificates by Hand (And What We Use Instead)
If you have shipped an iOS app, you know the feeling. The release is ready. The code is done. The testing is done. And then you spend the next two hours wrestling with:
- An expired distribution certificate you forgot to renew
- A provisioning profile that includes the wrong devices
- An App Store Connect upload that fails because Xcode’s credentials keychain is out of sync
- A new team member who needs to be added to the Apple Developer account before they can build at all
iOS certificate management is not technically hard. But it is relentlessly, consistently, reliably inconvenient. And it compounds: every new team member, every new device, every certificate renewal adds to the overhead.
We were building mobile apps for four clients simultaneously when we decided enough was enough.
What “Managing Certificates by Hand” Actually Looks Like
Before describing the fix, it is worth being precise about the problem. iOS code signing involves several distinct components that must stay in sync:
iOS code signing components:
1. Apple Developer Account
+-- Development certificates (for running on device)
+-- Distribution certificates (for App Store)
2. Provisioning Profiles
+-- Development profile (which devices can run the debug build)
+-- Distribution profile (which app ID + certificate for release)
3. Local Keychain (on each developer's machine)
+-- Private keys for each certificate
+-- Must be manually exported/imported for each developer
4. App Store Connect
+-- App record
+-- Build submission (via Xcode or Transporter)
+-- TestFlight configuration
All four must be aligned for every build.
When anything expires or changes, they all need updating.
For a team of one, this is manageable. For a team of three working across four simultaneous client apps, it was a constant source of friction.
A typical broken state we encountered more than once:
Timeline of a broken release:
Monday 9:00 AM -- Developer A tries to archive build for App Store
Monday 9:15 AM -- Archive fails: distribution certificate expired
Monday 9:30 AM -- Generate new distribution certificate on Apple portal
Monday 9:45 AM -- Download and install certificate on Developer A's machine
Monday 10:00 AM -- Archive succeeds on Developer A's machine
Monday 10:15 AM -- Developer B tries to build (different client app)
Monday 10:20 AM -- Build fails: Developer B's machine still has old certificate
Monday 10:30 AM -- Export certificate from Developer A, import on Developer B
Monday 11:00 AM -- Both machines working
Monday 11:15 AM -- CI/CD pipeline tries to build
Monday 11:20 AM -- Pipeline fails: certificate not in CI keychain
Monday 12:00 PM -- Certificate added to CI keychain, pipeline restarted
Monday 12:30 PM -- Build finally in TestFlight, 3.5 hours after starting
That pattern repeated on every certificate renewal — typically twice a year for each certificate, across multiple apps.
What EAS Build Does Differently
Expo Application Services (EAS) Build is a cloud build service that handles iOS and Android builds. The key difference from a certificate management perspective is where the credentials live:
Manual certificate management:
--------------------------------
Developer's machine (keychain)
AND CI machine (keychain)
AND other developer machines (keychain)
AND remembered to update after renewal?
= N machines x M certificates x renewal events
= Certificate sprawl
EAS Build credential management:
----------------------------------
EAS credential store (cloud, per project)
Every build pulls from the same credential store.
One update -> all builds immediately use new credential.
= 1 credential store x M certificates
= Certificate single source of truth
EAS Build can also automatically renew certificates. When a distribution certificate is approaching expiry, EAS generates a new one, stores it, and uses it for the next build — without any developer intervention.
What the Migration Looked Like
We migrated four active projects over a long weekend. Here is the process for each:
Step 1: Initialise EAS on the project
Migration checklist -- Step 1:
□ Install latest Expo CLI and EAS CLI
□ Run: eas build:configure
This creates eas.json with build profiles:
development (for simulator/device testing)
preview (for internal TestFlight)
production (for App Store submission)
□ Commit eas.json to repository
Step 2: Migrate credentials to EAS
Migration checklist -- Step 2:
□ Run: eas credentials
EAS CLI presents options:
a) Let EAS manage credentials automatically
-> EAS creates new certificates via Apple API
-> Recommended for new projects or clean migration
b) Import existing credentials
-> Upload your existing .p12 certificate + provisioning profile
-> Recommended if you have an active App Store presence
-> Avoids invalidating existing TestFlight builds
□ Verify: credentials visible in EAS dashboard
□ Remove certificates from local keychain (optional, after verification)
Step 3: Update CI/CD pipeline
Migration checklist -- Step 3:
□ Replace existing build steps with:
eas build --platform ios --profile production --non-interactive
□ Remove keychain setup steps from CI config
(no longer needed -- credentials are in EAS)
□ Remove certificate import/export steps
(no longer needed)
□ Add EAS_TOKEN to CI environment variables
(for EAS CLI authentication in non-interactive mode)
Step 4: Set up automatic submission
Migration checklist -- Step 4 (optional but recommended):
□ Configure eas.json submit section:
production:
ios:
appleId: your@apple.id
ascAppId: "1234567890" (App Store Connect app ID)
□ Run builds with:
eas build --platform ios --auto-submit
This builds AND submits to TestFlight automatically
No manual Xcode or Transporter upload needed
The Developer Experience After Migration
The difference in day-to-day developer experience was noticeable immediately.
Before (manual):
Developer workflow for release build:
1. Check certificate status (expired?)
2. If expired: renew on Apple portal (15 min)
3. Download certificate (5 min)
4. Install in keychain (5 min)
5. Check provisioning profile (still valid?)
6. If not: regenerate on Apple portal (10 min)
7. Archive in Xcode (15-25 min)
8. Validate archive in Xcode (5 min)
9. Upload via Transporter or Xcode (10-15 min)
10. Wait for App Store Connect processing (10-30 min)
Total: 75-120 minutes, with possibility of failure at any step
After (EAS):
Developer workflow for release build:
1. Run: eas build --platform ios --auto-submit --profile production
2. Wait for build completion notification (15-25 min in cloud)
3. Build appears in TestFlight automatically
Total: 2 minutes of active developer time
15-25 minutes of unattended cloud build time
New developers joining the project no longer need Apple Developer account access to build. They run eas build and the credentials come from the EAS store. The Apple portal configuration is managed once, not once per developer per machine.
Limitations and When Not to Use EAS
EAS Build is not the right choice in every situation:
Use EAS Build when:
[x] Team of 2+ developers
[x] Multiple client apps managed simultaneously
[x] Need for reproducible cloud builds (not "works on my machine")
[x] Want automated App Store submission
[x] Happy to accept Expo's build infrastructure
Consider alternatives when:
[ ] Custom native modules with complex build requirements
(EAS supports custom native code but some edge cases need care)
[ ] Proprietary or sensitive code that cannot leave your infrastructure
(EAS builds happen on Expo's servers)
[ ] Build speed is critical
(EAS queues can add wait time during peak periods;
a local Mac mini build farm may be faster for high-volume)
[ ] Fully custom Xcode build configurations
(EAS supports many configurations but not all)
For most React Native apps targeting the App Store, especially for Australian businesses building for iOS as a primary platform, EAS Build is the right default in 2026.
The Broader Lesson: Automation Has a Setup Cost, But Pays Off
The migration took a weekend. Four projects, documentation, CI/CD updates, verification. That was about 16 hours of work.
The savings:
Time saved per release cycle (per project):
Before: ~2 hours of certificate/build/upload work
After: ~2 minutes of active work
Saving per release: ~118 minutes (~2 hours)
Releases per month: ~4 (across 4 client projects)
Monthly time saving: ~8 hours
Annual time saving: ~96 hours
Payback period for 16-hour migration:
2 months
Beyond time savings, the reliability improvement was harder to quantify but real: we have not had a failed certificate-related release in the months since migration. That removes a class of production incidents entirely.
Getting Started
If you are currently managing iOS certificates manually and want to migrate to EAS:
Quick-start path:
1. Install EAS CLI: npm install -g eas-cli
2. Log in: eas login
3. Navigate to your React Native project
4. Initialise: eas build:configure
5. Start with a preview build to test the setup:
eas build --platform ios --profile preview
6. Verify the build appears in TestFlight
7. Migrate production workflow once preview is confirmed
The Expo documentation for EAS Build is comprehensive and kept current — it is worth reading from start to finish before migrating a production app.
If you are building mobile apps in Sydney and want expert guidance on the migration, the Awesome Apps team has done this migration for multiple client projects and can help you get it right the first time.
Related: Expo OTA Updates: Shipping Bug Fixes Without an App Store Review
For insights on building a technology strategy where mobile, web, and cloud work together, visit Ash Ganda’s blog.
This article is brought to you by Ganda Tech Services — Sydney’s complete digital solutions provider covering cloud, web, and mobile.
Talk to a Sydney app developer — free.
30 minutes. We'll tell you what your app needs, how long it takes, and what it costs. Real answers, no sales pitch.
Book Free App Strategy Call →Free · 30 minutes · No obligation