Skip to content
App Signing & PushPart 27 of 44
App SigningMobile DevelopmentFlutter

fastlane match: One Set of iOS Certificates for the Whole Team

fastlane match keeps one encrypted set of iOS certificates and profiles for the whole team. Storage options, match types, API keys, readonly CI setup and nuke.

By Bimal Khatri·14 min read·Sep 17, 2026·Updated Sep 17, 2026
fastlane match: One Set of iOS Certificates for the Whole Team

fastlane match keeps one shared, encrypted copy of your team's iOS signing certificates, private keys and provisioning profiles, in a private Git repository or a cloud bucket, and installs them on any Mac or CI machine that runs it. Instead of every developer making their own distribution certificate, the whole team signs with the same set.

The usual pattern: one person with Apple Developer access runs match to create or renew everything, and every other machine, CI included, runs it in readonly mode, which only downloads and installs. The files are protected by a passphrase, supplied on CI through the MATCH_PASSWORD environment variable.

The first sections explain the idea in plain terms. The rest covers storage choices, match types, the App Store Connect API key, CI setup, and nuke, the command that can take down builds people are already testing.

The problem match solves

An Apple Distribution certificate belongs to the team, and Apple limits how many of each distribution type a team may hold. Its private key, though, is created on one Mac. So the question every team hits is: how do the other Macs, and the build server, get that key?

The codesigning guide that match is built on describes what usually happens instead: each developer ends up with a separate signing identity, the account fills with dozens of profiles and duplicates, and every new device or expired certificate means renewing and downloading profiles by hand again.

match picks one answer and automates it. Think of a key safe in the office with a combination lock:

  • The safe is your storage: a private Git repository, a Google Cloud or Amazon S3 bucket, or GitLab.
  • Inside are the certificates, their private keys and the provisioning profiles.
  • The combination is the match passphrase, which encrypts the files (for Git and S3 storage).
  • Anyone given the storage access and the combination can open the safe and copy the keys into their own Keychain.

The analogy stops at one point: match is not only a safe. When run without readonly, it also talks to Apple to create certificates and profiles that are missing, and to renew profiles, then puts the results in the safe.

A map of how fastlane match moves signing files. A Mac with Apple Developer access runs match, which creates or renews certificates and profiles on Apple's developer portal and pushes them, encrypted, into the storage: a private Git repository with certs and profiles folders. Teammates' Macs and the CI runner run match in readonly mode, which decrypts the files and installs them without contacting Apple.

What ends up where

After the first run, the storage holds two folders:

  • certs: every certificate with its private key. match keeps the .cer and the private key (as a .p12) as separate files.
  • profiles: every provisioning profile.

match also writes a README.md into a Git repository to help new team members. In Google Cloud storage, the top-level folder is your Team ID.

On a machine that runs match, the private keys and certificates go into the Keychain, and the profiles go into ~/Library/Developer/Xcode/UserData/Provisioning Profiles (or ~/Library/MobileDevice/Provisioning Profiles for Xcode versions before 16). The fastlane docs warn that match picks the folder from the default Xcode on the machine, so if you switch Xcode versions in a lane, do it before match runs.

Profiles are named match + type + bundle ID, for example match AppStore com.example.app or match Development com.example.app, which makes them easy to select in Xcode.

Storage options

storage_modeWhere the files liveHow they are protectedKey options
git (default)A private Git repository you controlEncrypted by match with OpenSSL, using your passphrasegit_url, git_branch (default master)
google_cloudA Google Cloud Storage bucketGoogle-managed encryption; access through gc_keys.json or Google accountsgoogle_cloud_bucket_name, google_cloud_project_id
s3An Amazon S3 bucketEncrypted by match with your passphrase, unless you set s3_skip_encryptions3_bucket, s3_region, s3_access_key, s3_secret_access_key
gitlab_secure_filesGitLab's Secure Files for a projectGitLab's own storage; match does not add its passphrasegitlab_project, gitlab_host, job_token or private_token

A few notes from the fastlane documentation:

  • For Git, use a private repository, and switch on two-factor authentication for every account that can read it.
  • To keep several teams in one repository, give each team its own git_branch.
  • fastlane match migrate moves existing files from one storage backend to another.
  • Keep gc_keys.json out of version control, like any credential.

Setting up

Run this in your project folder:

fastlane match init

It asks which storage to use and writes a Matchfile. It does not touch Apple or your certificates yet, and it does not check the Git URL. A typical Matchfile:

git_url("git@github.com:example/certificates.git")
storage_mode("git")
type("development")
app_identifier(["com.example.app", "com.example.app.widget"])
team_id("A1B2C3D4E5")

List every bundle ID that needs a profile, including extensions such as widgets. match reuses one certificate across them and makes a separate profile for each.

If the account already has a tangle of old certificates, the fastlane docs suggest considering match nuke before the first run (read the section on it below first). If you would rather keep an existing certificate, fastlane match import asks for the .cer, the .p12 and the profiles, checks the certificate against the portal, and adds them to storage. With --skip_certificate_matching true it skips that check, for when there is no portal access; the docs ask you to be sure the files match the type and are neither revoked nor expired.

Then generate or fetch:

fastlane match development
fastlane match appstore

Match types

TypeCertificate it usesProfile it makesTypical use
developmentApple DevelopmentDevelopmentRunning on registered devices from Xcode
adhocApple DistributionAd HocTest builds for registered devices
appstoreApple DistributionApp StoreTestFlight and App Store uploads
enterpriseIn-house distributionIn-HouseApple Developer Enterprise Program apps
developer_idDeveloper ID ApplicationDeveloper IDMac apps outside the Mac App Store

Two more types, mac_installer_distribution and developer_id_installer, cover the certificates that sign Mac installer packages; the additional_cert_types option fetches them alongside a Mac app's certificate.

adhoc and appstore share the same distribution certificate; only the profiles differ. On a Mac with Xcode 11 or later, match creates the unified Apple Development and Apple Distribution certificates by default (generate_apple_certs). The difference between those two is covered in Apple Development vs Apple Distribution.

In a Fastfile, with an App Store Connect API key

match can log in with an Apple ID, but that involves two-factor authentication. fastlane recommends an App Store Connect API key instead, which needs no 2FA, and notes that provisioning needs a team key; an individual key cannot use the provisioning endpoints. How to create one is in App Store Connect API keys.

default_platform(:ios)

platform :ios do
  before_all do
    setup_ci
  end

  lane :beta do
    app_store_connect_api_key(
      key_id: ENV["ASC_KEY_ID"],
      issuer_id: ENV["ASC_ISSUER_ID"],
      key_content: ENV["ASC_KEY_P8_BASE64"],
      is_key_content_base64: true
    )

    match(
      type: "appstore",
      app_identifier: ["com.example.app", "com.example.app.widget"],
      readonly: is_ci
    )

    build_app(scheme: "Runner", export_method: "app-store")
    upload_to_testflight
  end
end

What each piece does:

  • app_store_connect_api_key builds the key from its Key ID, Issuer ID and .p8 contents (key_filepath works instead of key_content). Its duration option is capped at 1200 seconds, and in_house marks an Enterprise team. The key is stored in the lane context.
  • match picks that key up from the lane context automatically, unless you pass api_key_path. After it runs, it records which profile belongs to which bundle ID, and build_app uses that mapping when exporting.
  • readonly: is_ci means: on CI, only fetch; on a developer's Mac, create what is missing.
  • setup_ci is explained in the next section.

From the command line, the same key can come from a JSON file with key_id, issuer_id and key (the .p8 text):

fastlane match appstore --api_key_path fastlane/api_key.json --readonly

Running match on CI

A sequence chart of match on a CI runner. setup_ci creates a temporary keychain and turns on readonly mode. match clones the certificates repository using a deploy key or a token, decrypts the files with MATCH_PASSWORD, imports the certificate and private key into the temporary keychain, and installs the profiles. Then build_app signs the app. In readonly mode, match never contacts Apple.

Four things make it work.

1. Readonly mode. The fastlane docs recommend always using readonly on CI. In that mode match skips logging in to Apple entirely and only installs what is already in storage, so a CI job can never create, renew or revoke anything.

2. A keychain. A CI runner has no logged-in user keychain to put keys into. The setup_ci action creates a temporary keychain for match, points match at it, and switches match to readonly mode. It only acts when it detects a CI environment, unless you pass force: true.

3. The passphrase. For Git and S3 storage, set a secret environment variable named MATCH_PASSWORD. On a developer's Mac, match asks once and stores the passphrase in the local keychain. On CI there is nobody to ask, so a missing variable ends the run with a message that neither the environment variable nor the keychain contained a password.

4. Access to the storage. For Git, this is the fiddly part. The fastlane docs point out that GitHub will not accept the same deploy key on two repositories, and your CI's key is probably already used for the app's own repository. The options match offers:

OptionEnvironment variableWhat it takes
git_basic_authorizationMATCH_GIT_BASIC_AUTHORIZATIONBase64 of username:personal_access_token
git_private_keyMATCH_GIT_PRIVATE_KEYA path to a private key, or the key itself, for a separate deploy key
git_bearer_authorizationMATCH_GIT_BEARER_AUTHORIZATIONA bearer token, for example on Azure DevOps

The base64 value comes from:

echo -n your_github_username:your_personal_access_token | base64

For Google Cloud, provide gc_keys.json to the job at build time. For S3, provide the region, access key, secret key and bucket, with read access.

Adding devices and renewing

When a tester's device is registered, Ad Hoc and development profiles must be regenerated to include it. match can do that for you:

lane :adhoc_build do
  register_devices(devices_file: "./devices.txt")
  match(type: "adhoc", force_for_new_devices: true)
end

force_for_new_devices regenerates the profile when the number of enabled devices has changed. It is ignored for appstore and developer_id profiles, which hold no device list. force: true regenerates profiles on every run.

For expired certificates, renew_expired_certs removes the expired certificate from storage and creates a new one, but it does not revoke the old one in the portal; the option's help text suggests match nuke if you need the slot back. It also notes that renewing Developer ID certificates needs the Account Holder signed in with an Apple ID, because an API key cannot do it.

These write operations need a machine that is not in readonly mode, with portal access.

nuke, and why to be careful with it

match nuke revokes certificates and deletes provisioning profiles in your developer account, for one type at a time:

fastlane match nuke development
fastlane match nuke distribution
fastlane match nuke enterprise

A comparison chart of what nuke distribution and nuke enterprise affect. Nuke distribution revokes distribution certificates and deletes App Store and Ad Hoc profiles for every app; live App Store and TestFlight builds keep working, but Ad Hoc builds already installed stop working. Nuke enterprise revokes in-house certificates and deletes in-house profiles; installed in-house apps stop opening.

What the fastlane docs and the command's own warnings say:

  • It lists what it will remove and asks you to confirm, unless you pass --skip_confirmation.
  • nuke distribution deletes both App Store and Ad Hoc profiles. Its source code shows it also selects Developer ID profiles, and it removes the matching files from your storage.
  • Any app_identifier you set is ignored: it removes profiles for all your apps in the team, not just the current project.
  • Apps already on the App Store and in TestFlight keep working.
  • Builds distributed through Ad Hoc or Enterprise are disabled, and you have to ship new builds.
  • On an Enterprise account it shows an extra warning and asks again.
  • --safe_remove_certs removes certificates from your storage without revoking them in the portal.

Revoking an Enterprise distribution certificate stops every in-house app signed with it from opening on every device, so treat nuke enterprise as an outage. Read the list it prints before you confirm, and make sure no one else's project relies on the same team.

Other commands worth knowing

CommandWhat it does
fastlane match change_passwordRe-encrypts every file with a new passphrase. Every machine is asked for it on the next run
fastlane match decryptDecrypts the repository and leaves it on disk
match_file encrypt / match_file decryptEncrypts or decrypts a single file, asking for the passphrase
fastlane match importAdds an existing .cer, .p12 and profiles to storage
fastlane match migrateMoves files to another storage backend
fastlane action matchLists every option

Because match stores the certificate and private key separately, a tool that wants a single .p12 needs them recombined; the fastlane docs show the OpenSSL commands, and export a .p12 from Keychain covers the more common route.

Is it safe to keep signing keys in Git?

The fastlane docs make the case this way. For Git storage, every file is encrypted with your passphrase, and you control who can read the repository. A stolen App Store profile is of little use, because a build only reaches users after Apple re-signs it, which needs your App Store Connect login. Development and Ad Hoc profiles only work on registered devices, and adding devices needs your developer account. The real risk is Enterprise profiles, which can install on any device, so the docs ask for extra care there: a private repository and a strong passphrase.

One more change to know: match's template_name option, which produced profiles with extra managed capabilities, has been deprecated since Apple removed that undocumented function from its API in May 2025. match cannot currently produce such profiles.

Common mistakes

  • Running match without readonly on CI. A misconfigured job can create certificates or profiles you did not want. Use setup_ci, or readonly: is_ci.
  • Forgetting MATCH_PASSWORD on CI. The run stops because nobody can type the passphrase.
  • Using an individual API key. Provisioning needs a team key.
  • Not selecting the match profile in Xcode. For match's profiles, the fastlane docs describe turning off automatic signing and choosing the profile, such as match AppStore com.example.app, for each target.
  • Forgetting extension bundle IDs. Each widget or notification extension needs its own profile, so list them all in app_identifier.
  • One branch for two teams. Use a separate git_branch per team.
  • Assuming renew_expired_certs frees a certificate slot. It does not revoke the old certificate.
  • Running nuke to tidy one app. It ignores app_identifier and affects every app in the team.
  • Committing gc_keys.json or an API key JSON file. Treat both like the certificates themselves.

Questions people ask

What does fastlane match do?

It creates your iOS and Mac signing certificates and provisioning profiles, stores them encrypted in a place your team shares, and installs them on any machine that runs it. Everyone signs with the same set.

Where does fastlane match store certificates?

In the storage you choose: a private Git repository (the default), a Google Cloud Storage bucket, an Amazon S3 bucket, or GitLab Secure Files. On each machine, keys go into the Keychain and profiles into Xcode's provisioning profiles folder.

What is MATCH_PASSWORD?

The passphrase match uses to encrypt and decrypt files in Git and S3 storage. Set it as a secret environment variable on CI, where match cannot prompt for it.

Does fastlane match need my Apple ID?

Not in readonly mode, which never logs in to Apple. To create or renew anything, it needs either an Apple ID or an App Store Connect API key; fastlane recommends a team API key.

What does fastlane match readonly do?

It only downloads and installs the certificates and profiles already in storage. It never creates, renews or revokes anything, and it does not contact Apple.

Is fastlane match nuke safe?

Only if you understand its reach. It revokes certificates and deletes profiles for every app in the team, for the type you name. App Store and TestFlight builds keep working; Ad Hoc and Enterprise builds stop.

Can I use existing certificates with fastlane match?

Yes. fastlane match import adds an existing certificate, its private key and profiles to your storage.

How do I add a new device to match profiles?

Register the device, then run match for adhoc or development with force_for_new_devices: true, or pair it with the register_devices action in a lane.

Sources

Keep reading

More writing

Keep reading