Overview
match is a new approach to iOS code signing: share one code signing identity across your development team to simplify your codesigning setup and prevent code signing issues.
match stores your certificates and profiles in a git repo, encrypted and synced across your team.
Why match?
The Problem
Every team member has their own code signing identity
Certificates and profiles expire randomly
Setting up new machines is complicated
Certificates often conflict between team members
The Solution
match creates one shared identity stored in a private git repository, encrypted with a passphrase.
Installation
match is part of fastlane. Install it with:
Quick Start
Create a git repository
Create a private git repository to store certificates. # On GitHub, GitLab, or Bitbucket
# Make sure it's private!
Initialize match
This creates a Matchfile with your git repo URL.
Generate certificates
fastlane match appstore
fastlane match development
This generates and encrypts certificates in your git repo.
Use on other machines
match downloads and installs certificates automatically.
How match Works
Generate certificates
match generates certificates and provisioning profiles on the Apple Developer Portal.
Encrypt and store
Certificates are encrypted with a passphrase and committed to your git repository.
Install on team machines
Team members run match to download, decrypt, and install certificates in their keychain.
CLI Commands
Development
Ad-Hoc
App Store
Enterprise
Initialize
Change Password
Nuke Certificates
fastlane match development
Certificate Types
Creates iOS/tvOS/macOS development certificates and profiles. fastlane match development
Creates ad-hoc distribution certificates and profiles.
Creates App Store distribution certificates and profiles.
Creates enterprise distribution certificates and profiles. fastlane match enterprise
Creates Developer ID certificates (macOS). fastlane match developer_id
Key Options
URL to the git repo containing certificates. match (
type: "appstore" ,
git_url: "https://github.com/company/certificates.git"
)
The bundle identifier(s) of your app. match (
type: "appstore" ,
app_identifier: "com.example.app"
)
Or multiple: match (
type: "appstore" ,
app_identifier: [
"com.example.app" ,
"com.example.app.watchkit"
]
)
type
string
default: "development"
Define the profile type (development, adhoc, appstore, enterprise).
The ID of your Developer Portal team. match ( team_id: "Q2CBPJ58CA" )
Only fetch existing certificates, don’t generate new ones. match (
type: "appstore" ,
readonly: true
)
Renew provisioning profiles every time. match (
type: "appstore" ,
force: true
)
Renew profiles when device count changes. match (
type: "development" ,
force_for_new_devices: true
)
Specific git branch to use. match (
type: "appstore" ,
git_branch: "production"
)
Use basic authorization to access the git repo. match (
type: "appstore" ,
git_basic_authorization: ENV [ "GIT_TOKEN" ]
)
keychain_name
string
default: "login.keychain"
Keychain to import certificates to. match (
type: "appstore" ,
keychain_name: "fastlane.keychain"
)
Password for the keychain. match (
type: "appstore" ,
keychain_name: "fastlane.keychain" ,
keychain_password: ENV [ "KEYCHAIN_PASSWORD" ]
)
Set the platform (ios, tvos, macos, catalyst). match (
type: "appstore" ,
platform: "macos"
)
Storage backend (git, google_cloud, s3, gitlab_secure_files). match (
type: "appstore" ,
storage_mode: "s3" ,
s3_bucket: "my-certificates"
)
Using with fastlane
Basic Usage
lane :beta do
match ( type: "appstore" )
gym
pilot
end
Development Build
lane :dev do
match ( type: "development" )
gym ( export_method: "development" )
end
Multiple App Identifiers
lane :release do
match (
type: "appstore" ,
app_identifier: [
"com.example.app" ,
"com.example.app.watchkit" ,
"com.example.app.watchkit.extension"
]
)
gym
deliver
end
CI/CD Setup
lane :ci_build do
create_keychain (
name: "ci.keychain" ,
password: ENV [ "KEYCHAIN_PASSWORD" ],
default_keychain: true ,
unlock: true ,
timeout: 3600
)
match (
type: "appstore" ,
readonly: true ,
keychain_name: "ci.keychain" ,
keychain_password: ENV [ "KEYCHAIN_PASSWORD" ]
)
gym
end
Storage Backends
Git (Default)
match (
type: "appstore" ,
git_url: "https://github.com/company/certificates.git" ,
git_branch: "main"
)
Google Cloud Storage
match (
type: "appstore" ,
storage_mode: "google_cloud" ,
google_cloud_bucket_name: "my-certificates" ,
google_cloud_keys_file: "./gc_keys.json"
)
Amazon S3
match (
type: "appstore" ,
storage_mode: "s3" ,
s3_bucket: "my-certificates" ,
s3_region: "us-east-1"
)
GitLab Secure Files
match (
type: "appstore" ,
storage_mode: "gitlab_secure_files" ,
gitlab_project: "gitlab-org/gitlab"
)
Matchfile
Create a Matchfile for persistent configuration:
# Matchfile
git_url "https://github.com/company/certificates.git"
git_branch "main"
type "appstore"
app_identifier [
"com.example.app" ,
"com.example.app.watchkit"
]
username "[email protected] "
team_id "Q2CBPJ58CA"
keychain_name "login.keychain"
Generate a template:
Nuke Command
Destructive operation : The nuke command permanently deletes certificates from the Developer Portal.
Revoke and delete all certificates:
# Delete development certificates
fastlane match nuke development
# Delete distribution certificates
fastlane match nuke distribution
# Delete enterprise certificates
fastlane match nuke enterprise
Then regenerate:
fastlane match development
fastlane match appstore
Change Password
Re-encrypt all files with a new password:
fastlane match change_password
Environment Variables
Certificate type (development, adhoc, appstore)
Passphrase to decrypt certificates
Tips & Best Practices
Use private repository : Always use a private git repository to store certificates.
Save your passphrase : Store the encryption passphrase in a secure password manager.
One identity per team : All team members share the same certificates - no more conflicts!
CI readonly mode : Use readonly: true on CI to prevent generating new certificates.
Troubleshooting
Certificate already exists
Use readonly mode to fetch existing certificates:
match ( type: "appstore" , readonly: true )
Wrong passphrase
The passphrase is the one you set when first running match. Store it securely:
export MATCH_PASSWORD = "your-passphrase"
Device limit reached
Use nuke to clean up and regenerate:
fastlane match nuke development
fastlane match development
Git authentication failed
Use a personal access token:
match (
type: "appstore" ,
git_basic_authorization: Base64 . strict_encode64 ( "username:token" )
)
cert - Alternative certificate management
sigh - Alternative provisioning profile management
gym - Build your app with match certificates
pilot - Upload to TestFlight