Setting up fastlane on a dedicated Mac build server
On a Mac build server, install fastlane with Bundler, which fastlane prefers, or with Homebrew. Keep signing in match, run it readonly on CI, and log in to Apple with an App Store Connect API key. Start CI lanes with setup_ci, so fastlane uses a temporary keychain. Your CI job then runs one command: bundle exec fastlane beta.
What you need first
- A Mac with Xcode, admin access and Homebrew.
- A CI agent on that Mac: GitHub Actions, GitLab, Buildkite, Jenkins or CircleCI.
- An App Store Connect account with the right to create API keys.
- A private Git repo for match, or a Google Cloud or S3 bucket.
1. Install fastlane
fastlane's docs, read in October 2026, prefer Bundler. They support Ruby 3.2 or newer and prefer 3.3 or newer. They advise against macOS's system Ruby. Install a current Ruby with a version manager such as rbenv. Then add a Gemfile to the project:
source "https://rubygems.org" gem "fastlane"
gem install bundler bundle update git add Gemfile Gemfile.lock
Bundler pins the fastlane version in Gemfile.lock. Every machine and every CI run gets the same one. Run it as bundle exec fastlane.
The simpler route on a single build server is Homebrew. It brings its own Ruby:
brew install fastlane
Pick one route per project. Mixing them is how two fastlane versions end up on one machine.
2. Create an App Store Connect API key
fastlane recommends API keys over Apple ID logins: no two-factor prompt, better speed, more reliability. In App Store Connect, open Users and Access, then Integrations, then App Store Connect API. Create a Team Key. fastlane notes that provisioning calls need a team key. Give it the smallest role that works. Note the Issuer ID and Key ID. Download the .p8 file right away, because Apple lets you download it only once.
mkdir -p ~/.appstoreconnect mv ~/Downloads/AuthKey_ABC123XYZ.p8 ~/.appstoreconnect/ chmod 600 ~/.appstoreconnect/AuthKey_ABC123XYZ.p8
Keep the key on the build Mac or in your CI's secret store. Never commit it.
3. Set up match once, from a developer machine
bundle exec fastlane match init bundle exec fastlane match development bundle exec fastlane match appstore
match init asks where to store certificates and writes a Matchfile. The next two commands create certificates and profiles and save them, encrypted, to that storage. Choose a strong passphrase. CI reads it from the MATCH_PASSWORD variable.
fastlane recommends readonly mode on every CI system. Then CI only downloads what exists and never creates or revokes anything.
4. Write the lanes
default_platform(:ios)
platform :ios do
lane :test do
run_tests(
scheme: "MyApp",
devices: ["iPhone 17"],
result_bundle: true
)
end
lane :beta do
setup_ci
api_key = app_store_connect_api_key(
key_id: ENV["ASC_KEY_ID"],
issuer_id: ENV["ASC_ISSUER_ID"],
key_filepath: ENV["ASC_KEY_PATH"]
)
match(type: "appstore", readonly: is_ci, api_key: api_key)
increment_build_number(
build_number: latest_testflight_build_number(api_key: api_key) + 1
)
build_app(scheme: "MyApp")
upload_to_testflight(api_key: api_key, skip_waiting_for_build_processing: true)
end
endsetup_ci creates a temporary keychain, switches match to readonly, and sets up log and test result paths. It acts only when fastlane detects a CI run. If yours is not detected, pass force: true. Skipping build processing ends the job sooner, but fastlane notes it also skips distribution to external testers.
5. Call it from CI
Every CI system runs the same shell lines. Store the secrets in your CI, not in the repo.
export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 bundle install bundle exec fastlane beta # needs MATCH_PASSWORD, ASC_KEY_ID, ASC_ISSUER_ID and ASC_KEY_PATH set as CI secrets
On GitHub Actions these lines go in a run step, with the secrets passed in as env. A MacRun Mac comes with the GitHub Actions runner software already installed. Our GitHub Actions iOS setup guide shows a full workflow. On GitLab, Buildkite or CircleCI, put the same lines in the job's script. On Jenkins, use an sh step.
How this survives reboots
fastlane is not a service. It runs inside your CI agent's job. What must survive a reboot is the agent and its login session. Set up the agent as a LaunchAgent with automatic login, as in our GitLab Runner and Buildkite guides. The temporary keychain from setup_ci is rebuilt on every run. A locked login keychain after a restart cannot stall it.
Common errors and fixes
These are documented by fastlane.
- Odd encoding errors or crashes in CI. fastlane needs a UTF-8 locale. Set
LANGandLC_ALLtoen_US.UTF-8. - The job hangs at a two-factor prompt. Use the API key. With an Apple ID, set
SPACESHIP_ONLY_ALLOW_INTERACTIVE_2FAso it fails fast instead. - match tries to create new certificates on CI. Use
readonly, or callsetup_cifirst. - match cannot clone its repo. GitHub will not accept one deploy key on two repos. Use a read-only machine account, or
git_private_key, orMATCH_GIT_BASIC_AUTHORIZATION. - Profiles are installed but Xcode cannot see them. Xcode 16 moved the profiles folder. fastlane follows the selected Xcode, so run
xcode_selectbefore match on machines with two Xcodes.
Why a dedicated Mac helps
build_app and run_tests are Xcode builds. On a machine that keeps DerivedData and installed gems, they are much faster. In our benchmark on the Wikipedia iOS app with Xcode 26.6, a clean build took 86 seconds on an M6. It took 183 seconds on a GitHub-hosted macos-26 runner. A job after a small change took 27 seconds warm, against 269 fresh.
When you do not need a build server
If you ship to TestFlight once a week, a hosted runner or Xcode Cloud is less work. See our Xcode Cloud alternative guide for that trade. Also note what MacRun does not do. We do not manage code signing for you. You run match yourself, as on this page. We also offer no SLA.
Ready for a build server? Pricing lists every tier, and the setup docs show how to connect.
Frequently asked questions
Should I install fastlane with Homebrew or Bundler on a build server?
+
fastlane prefers Bundler, because Gemfile.lock pins the version. Homebrew is simpler on a single machine and brings its own Ruby.
What does fastlane setup_ci do?
+
It creates a temporary keychain, switches match to readonly, and sets up log and test result paths. It only acts on CI unless you pass force: true.
Why use an App Store Connect API key with fastlane?
+
It needs no two-factor prompt, and fastlane calls it faster and more reliable than an Apple ID session. Create a Team Key for provisioning access.
Should match run in readonly mode on CI?
+
Yes. fastlane recommends it, so CI only downloads existing certificates and profiles and never creates or revokes them.