
An expired Apple distribution certificate stops App Store Connect uploads the moment it lapses. This page is the runbook: rotate during business hours, keep in-flight CI builds green, and hold an encrypted rollback asset in case the new certificate fails verification. Commands below come from the primary docs at Apple Developer certificates and Fastlane match.
Apple's certificates page is explicit about the constraints you have to design around:
Because only one Apple Distribution certificate is active per team, you cannot run two overlapping App Store distribution certs in parallel the way you might with Developer ID. The "overlap window" here is a time overlap — generate the replacement before the old one expires — not two simultaneously trusted certificates.
Put a calendar alert 30 days before expiry and use the time to:
.p12 of the current certificate and private key for rollback.Automate the alert with a weekly check that fails CI when the cert has 30 days or less left:
#!/usr/bin/env bash
# cert-expiry-check.sh — run weekly on a macOS runner
set -euo pipefail
: "${CERT_P12_PATH:?set CERT_P12_PATH}"
: "${CERT_P12_PASSWORD:?set CERT_P12_PASSWORD via CI secret}"
EXPIRY=$(openssl pkcs12 -in "$CERT_P12_PATH" -nokeys -passin "pass:${CERT_P12_PASSWORD}" \
| openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -j -f "%b %e %T %Y %Z" "$EXPIRY" +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - $(date +%s)) / 86400 ))
printf 'Distribution cert expires: %s (%d days)\n' "$EXPIRY" "$DAYS_LEFT"
[ "$DAYS_LEFT" -gt 30 ] || { echo "Rotate now: $DAYS_LEFT days left" >&2; exit 1; }
Read CERT_P12_PASSWORD from the CI secret manager — never hard-code it. This is an unexecuted example; run it against your own encrypted .p12 on a macOS runner and confirm the output before scheduling.
Per the Fastlane match docs, match nuke "revokes your certificates and provisioning profiles" for the chosen environment, and readonly mode "only fetch[es] existing certificates and profiles, don't generate new ones" — Fastlane recommends readonly on CI. --force renews provisioning profiles every run; it does not by itself revoke a certificate.
The clean, repeatable rotation on a macOS workstation (not on CI):
# 1. Revoke the current distribution cert and its App Store profiles
fastlane match nuke distribution # confirms interactively; review the list before approving
# 2. Regenerate a fresh distribution cert plus App Store profiles and commit to the Match repo
fastlane match appstore
# 3. Optional: refresh development material at the same time
fastlane match development
Match writes the regenerated assets to its Git repository in a single commit. CI runners pick up the new cert on their next match ... readonly fetch. match nuke distribution also invalidates existing App Store provisioning profiles tied to the revoked cert; match appstore regenerates them. Do not run nuke on an Enterprise (in-house) program casually — revoking that distribution cert blocks previously installed in-house apps from launching.
Run in order. Do not skip step 4.
| # | Step | Command or check |
|---|---|---|
| 1 | Pre-flight: Match reachability | fastlane match appstore --readonly from one of each runner type |
| 2 | Pre-flight: freeze releases | Hold merges to the release branch; announce the window |
| 3 | Rollback asset | Export current cert + private key as an encrypted .p12; store in the secret manager, not in Git |
| 4 | Drain CI | Wait for all in-flight builds to finish; pause new jobs on release lanes |
| 5 | Rotate | fastlane match nuke distribution then fastlane match appstore on an admin macOS machine |
| 6 | Verify build | Trigger a signed Release archive on a non-production branch |
| 7 | Verify upload | fastlane pilot upload to a TestFlight internal group |
| 8 | Resume | Unfreeze releases |
| 9 | Reset alert | Update the 30-day calendar entry and the weekly script baseline |
Rollback path. The window is narrow: once match nuke revokes the old cert, previously signed in-flight builds that have not been uploaded may be marked Invalid Binary. If the new cert fails step 6 before you nuke, abort — nothing has changed on Apple's side. If it fails after step 5, re-import the encrypted .p12 from step 3 on an admin machine, upload the preserved public certificate back to the Apple Developer portal only if Apple still accepts it, then re-run fastlane match appstore pointed at the restored material. If Apple will not reinstate the revoked certificate, your fastest recovery is finishing the new-cert path: re-run match appstore, re-sign the pending build, and push.
Store the Match encryption password in a CI secret named MATCH_PASSWORD; Fastlane reads it from the environment. Use readonly on every CI job:
# Fastfile — CI-side lane; interactive rotation is a separate admin lane
lane :sync_signing do
match(type: "appstore", readonly: is_ci)
end
Do not echo the password, do not write a plaintext dump of the full environment to disk, and keep the signing job restricted to trusted branches. If you need to move a secret between systems, transfer the name list and restore values through the secret manager.
The rotation itself needs a Mac with the Match repo cloned, Fastlane installed, and the right Xcode for your target. Figment's options:
security, codesign and Xcode toolchains that still support Intel all work. It can host the weekly expiry check, run readonly match fetches, and sign supported Intel-compatible targets. ARM-only execution, GPU tasks and Xcode 27 are out of scope: Apple's porting guide says to use Xcode 26 or earlier on Intel. Access is via the browser console; inbound SSH is not available today.Assume one admin Mac for the rotation plus one CI runner. A straight read of the price list:
Physical Intel and M-series plans currently collect requests until supply is confirmed; agree an evaluation window in the quote if your rotation deadline is tight. See /mac-hosting and start a quote at /mac-hosting/start. Tax and any fees apply as stated on the actual quote.
Related guides: