Code Signing Setup via Fastlane Match
iOS certificates are source of half of CI incidents in mobile teams. Developer left and took Distribution Certificate created only on their machine. Certificate expired Friday evening before release. In CI, No matching provisioning profiles found—no one understands why it works locally but not in pipeline. Fastlane Match solves these via single encrypted repository for entire team.
How Match Works
Match stores certificates (.cer, .p12) and provisioning profiles (.mobileprovision) in encrypted Git repository or S3/Google Cloud Storage. Running match on new machine or in CI:
- Clones repository
- Decrypts files with password (MATCH_PASSWORD)
- Imports certificate into temporary keychain
- Installs provisioning profile
No manual downloading from Apple Developer Portal.
Initial Setup
# Initialize
fastlane match init
# Create certificates and profiles (once)
fastlane match development
fastlane match appstore
fastlane match adhoc
Matchfile:
git_url("[email protected]:MyOrg/ios-certificates.git")
storage_mode("git")
type("appstore")
app_identifier(["com.myapp.ios", "com.myapp.ios.extension"])
username("[email protected]")
Important: email must belong to account with App Manager role minimum. Better—create separate CI user in App Store Connect—then rotating personal account passwords won't break pipeline.
Usage in CI
lane :release do
setup_ci # creates temporary keychain, sets MATCH_KEYCHAIN_NAME
match(
type: "appstore",
readonly: true,
git_url: ENV["MATCH_GIT_URL"],
password: ENV["MATCH_PASSWORD"],
keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"]
)
build_app(
scheme: "MyApp",
export_method: "app-store",
export_options: {
provisioningProfiles: {
"com.myapp.ios" => "match AppStore com.myapp.ios"
}
}
)
end
readonly: true critically important in CI—without it match tries to create new certificate if not found, causing conflict with existing in Apple Developer Portal.
Certificate Rotation
Distribution certificates live 1 year. 30 days before expiration run fastlane match --force_for_new_devices or fastlane match nuke + recreate. nuke revokes all certificates of that type—do only if you understand consequences. After recreation all machines/CI automatically get new certificate on next match.
Multiple Applications and Extensions
If project contains App Extensions (Share Extension, Notification Service Extension, Widget), each target needs separate provisioning profile with corresponding app_identifier. Match supports array: app_identifier(["com.myapp", "com.myapp.share", "com.myapp.widget"]).
Alternative: Xcode Automatic Signing in CI
Apple added xcodebuild -allowProvisioningUpdates—Xcode itself requests profiles via API. Works, but requires Apple ID in CI agent keychain and unsuitable for GitHub Actions/GitLab CI without macOS self-hosted agent with persistent storage.
Process
Create separate CI Apple ID → initialize match repository → match init with needed types → test on local machine → integrate in Fastfile → add secrets to CI → test in pipeline.
Timeline: 1–3 days. Most time spent on initial certificate and profile creation, especially if project contains multiple targets. Cost calculated individually.







