Building a Production-Ready iOS CI/CD Pipeline with GitHub Actions, .NET MAUI & Fastlane
Building a Production-Ready iOS CI/CD Pipeline with GitHub Actions, .NET MAUI & Fastlane
Modern mobile applications need more than a successful build. A reliable release process should provide repeatability, security, automated signing, controlled deployments, and traceability.
For an iOS application built with .NET MAUI, GitHub Actions combined with a self-hosted macOS runner and Fastlane can provide a powerful CI/CD platform.
This article walks through the key components of a production-oriented iOS pipeline and the lessons that can be applied to other mobile DevOps environments.
1. The CI/CD Architecture
A clean pipeline should separate the release process into logical stages.
A typical flow is:
Source Code → Environment Preparation → Dependency Restore → Signing → Build → Artifact → Approval → TestFlight
The workflow discussed here separates the environment setup, iOS build, and TestFlight upload into different jobs. The build job depends on environment preparation, while the TestFlight job depends on a successful iOS build. This separation makes troubleshooting and maintenance much easier.
2. Why Use a Self-Hosted macOS Runner?
iOS development requires Apple’s build ecosystem, including Xcode and Apple’s signing infrastructure.
A self-hosted macOS runner provides greater control over:
- Xcode versions
- Installed SDKs
- .NET SDK versions
- Ruby and Fastlane
- Keychains
- Build caches
- Network connectivity
- Internal package repositories
The pipeline explicitly switches the runner to Xcode 26.5 before continuing with the build process.
This is especially useful when an organization needs a predictable and controlled build environment.
3. Keep the .NET Environment Predictable
One of the common problems with self-hosted runners is environment drift.
Different workflow runs can accidentally use different versions of .NET, global.json files, cached SDKs, or architecture-specific binaries.
The pipeline removes existing .NET installations and then installs a specific SDK version:
.NET SDK 9.0.305
It also verifies the installed architecture before continuing. This is an important DevOps principle:
A build server should be predictable, not dependent on whatever happens to be installed on it.
4. Dependency Management Matters
The application uses .NET MAUI workloads and restores the required dependencies before the actual iOS build.
The pipeline installs MAUI workloads including Android, iOS and WebAssembly tooling and restores the project dependencies.
Internal NuGet repositories are also configured for organization-specific packages.
This approach allows the pipeline to work with both public and private dependencies while keeping the package configuration inside the automated build process.
5. iOS Code Signing — The Critical Part
Building an iOS application is only part of the problem.
For distribution, the application must be correctly signed and associated with an appropriate provisioning profile.
The pipeline creates a temporary keychain specifically for the build:
- Create keychain
- Set it as the default
- Unlock it
- Configure its timeout
- Add it to the keychain search path
This is much safer than relying blindly on whatever happens to exist in the runner’s default login keychain.
The pipeline then uses Fastlane Match to retrieve the signing assets.
6. Verify Signing Assets Before Building
One of the most important lessons from iOS CI/CD troubleshooting is:
Don’t wait for dotnet publish to discover that signing is broken.
The workflow explicitly checks:
- Code-signing identities
- Installed provisioning profiles
- Provisioning profile names
before starting the build.
This makes failures easier to diagnose.
Instead of receiving a generic build failure, the pipeline can tell us whether the required certificate or provisioning profile is actually available.
7. Build for a Physical iOS Device
The pipeline uses:
dotnet publish -f net9.0-ios -c Release -r ios-arm64
and provides the signing identity, provisioning profile and Apple development team information during the build.
The important point is that signing assets must already be available before dotnet publish executes because the publishing process invokes the Apple build/signing toolchain.
8. Separate Build and Deployment
A good CI/CD pipeline should not automatically deploy every successful build to production distribution services.
The workflow creates an artifact after the iOS build.
The next job downloads that artifact and performs a separate TestFlight deployment.
This provides a clean separation:
Build → Artifact → Approval → Deployment
That separation is valuable because the same artifact that was tested can be promoted without rebuilding the application.
9. Add an Approval Gate
The workflow includes a manual approval input before the TestFlight upload.
The deployment proceeds only when the approval value is:
yes
Otherwise, the deployment stops.
This is a simple but effective release-control mechanism.
For organizations with formal release processes, this can be extended with environment approvals, change-management systems, or other governance controls.
10. Use App Store Connect API Authentication
The pipeline uses an App Store Connect API key rather than relying on interactive Apple credentials.
The API private key is temporarily decoded into AuthKey.p8 and used during the TestFlight deployment.
The API key is then explicitly removed after deployment.
This follows an important security principle:
Secrets should have the shortest practical lifetime.
11. Clean Up Persistent Self-Hosted Runners
Self-hosted runners are different from ephemeral GitHub-hosted runners.
Files can remain between workflow executions.
The cleanup stage therefore removes:
- Temporary keychains
- Provisioning profiles
- .NET installations
- Build outputs
- Temporary configuration files
This prevents one build from unintentionally affecting another.
It also reduces the risk of sensitive signing material remaining on the server.
12. Lessons for DevOps Engineers
There are several broader lessons from this pipeline.
Infrastructure consistency
Pin important tool versions such as Xcode and .NET rather than relying on whatever happens to be installed.
Security by design
Use secrets management, temporary keychains and short-lived authentication files.
Fail early
Validate certificates and provisioning profiles before starting an expensive application build.
Separate responsibilities
Keep environment preparation, building and deployment as independent pipeline stages.
Artifact-based deployment
Build once and deploy the generated artifact rather than rebuilding for every environment.
Self-hosted runner hygiene
Always assume that a self-hosted runner contains leftovers from previous executions.
Conclusion
A reliable iOS CI/CD pipeline is not simply a command that runs dotnet publish.
It is an engineered system that combines:
GitHub Actions + macOS + Xcode + .NET MAUI + Fastlane + Apple Signing + App Store Connect
The real value comes from making the entire release process repeatable, secure, observable and predictable.
When these principles are applied correctly, developers can focus on application development while DevOps automation handles the complex path from source code to TestFlight.
Build once. Validate early. Sign securely. Deploy with confidence.