Figment.so
Mac hostingWebsite hostingGuidesHow to usePricing

Export and Manage Xcode Archives Remotely

Release managers: keep your .xcarchive bundles on a hosted Mac and re-export signed IPAs with xcodebuild -exportArchive. A mid-release signing swap should never require a fresh compile. On Figment this normally runs on an M-series Mac from $95/month (specific chip, RAM and disk confirmed in the quote); the Managed Build Mac at $249/month adds CI setup, backups, monitoring, and 30 minutes of monthly support on one M-series Mac if you want the signing environment looked after for you. The $29 virtual Intel macOS plan (2 vCPU, 8 GiB RAM, 80 GiB disk, no GPU/audio) can run an Intel-compatible Xcode toolchain (Xcode 26 or earlier per Apple's Porting your macOS apps to Apple silicon) for signing and export workflows that don't need Apple-silicon execution. The authoritative commands below come from Apple's Distributing your app for beta testing and releases and Technical Note TN2339.

Where Xcode Stores Archives on Disk

Xcode writes every archive you produce under a date-partitioned directory inside your user library:

~/Library/Developer/Xcode/Archives/
  2026-11-10/
    MyApp 2026-11-10 14.23.11.xcarchive/
  2026-11-09/
    MyApp 2026-11-09 09.01.44.xcarchive/

An .xcarchive is a bundle (a directory with a known structure), not a flat file:

MyApp.xcarchive/
  Info.plist          — archive metadata: scheme, creation date, app version
  Products/
    Applications/
      MyApp.app       — the compiled binary with its frameworks
  dSYMs/
    MyApp.app.dSYM    — debug symbols for crash reporting
  BCSymbolMaps/       — bitcode symbol maps (older Xcode projects only)

Products/Applications/MyApp.app is the compiled binary; the dSYMs are what your crash reporter needs to symbolicate stack traces. Keep both together when you copy archives off to a file server or object store.

Listing and Inspecting Archives via CLI

On the hosted Mac, open a terminal (the browser console on the virtual Intel VM, or the desktop terminal on a physical M-series host) and work from the archives directory:

# List all archives, most recent first
find ~/Library/Developer/Xcode/Archives \
  -name "*.xcarchive" \
  -maxdepth 2 \
  | sort -r

# Show the archived app version and build number
/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleShortVersionString" \
  ~/Library/Developer/Xcode/Archives/2026-11-10/*.xcarchive/Info.plist

# Check the codesign identity currently embedded in the archive
codesign -dv --verbose=4 \
  ~/Library/Developer/Xcode/Archives/2026-11-10/*.xcarchive/Products/Applications/MyApp.app \
  2>&1 | grep -E "Authority|TeamIdentifier|Identifier"

The codesign output tells you which signing identity and team were used when the archive was originally built. If you are re-signing for a different distribution channel, verify the replacement identity is already in the Mac's keychain before you export.

Move signing material onto the hosted Mac through an encrypted PKCS#12 file: export the certificate and private key from the source keychain with a strong passphrase, transfer the .p12 over a channel that protects the file, and import it with security import into a dedicated keychain or by opening it from the desktop. Never write the private key to a plaintext file. Provisioning profiles are separate .mobileprovision files; copy them to ~/Library/MobileDevice/Provisioning Profiles/—they are not stored in Keychain. For CI, keep secrets in your CI secret manager and reference them by name; do not commit an env dump that might include unrelated tokens.

Re-Exporting an Archive with a Different ExportOptions.plist

The shell function below is the unique artifact for this guide. It takes an existing .xcarchive, an ExportOptions.plist, and an output directory, and produces a freshly signed IPA using the current signing identity and profile in the keychain:

#!/usr/bin/env bash
# re_export.sh — re-export an existing .xcarchive to a new IPA.
# Usage: bash re_export.sh /path/to/MyApp.xcarchive /path/to/ExportOptions.plist /path/to/output/
set -euo pipefail

ARCHIVE_PATH="$1"
EXPORT_PLIST="$2"
OUTPUT_DIR="$3"

[[ -d "$ARCHIVE_PATH" ]] || { echo "ERROR: archive not found at $ARCHIVE_PATH" >&2; exit 1; }
[[ -f "$EXPORT_PLIST"  ]] || { echo "ERROR: ExportOptions.plist not found at $EXPORT_PLIST" >&2; exit 1; }

mkdir -p "$OUTPUT_DIR"

echo "==> Re-exporting $(basename "$ARCHIVE_PATH")"
xcodebuild -exportArchive \
  -archivePath "$ARCHIVE_PATH" \
  -exportPath "$OUTPUT_DIR" \
  -exportOptionsPlist "$EXPORT_PLIST"

echo "==> IPA written to $OUTPUT_DIR"
ls -lh "$OUTPUT_DIR"/*.ipa

A minimal ExportOptions.plist for App Store Connect distribution:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>method</key>
  <string>app-store-connect</string>
  <key>teamID</key>
  <string>YOURTEAMID</string>
  <key>uploadSymbols</key>
  <true/>
</dict>
</plist>

Current Xcode 16 export methods (from local xcodebuild -help on Xcode 16.4) are app-store-connect, release-testing, enterprise, debugging, developer-id, mac-application, and validation; app-store, ad-hoc, and development are deprecated aliases. Confirm the exact string accepted by your installed Xcode with xcodebuild -help | sed -n '/exportOptionsPlist/,/^ *$/p' before you commit the plist. For an Ad Hoc build you also map each bundle ID to a profile:

<key>method</key>
<string>release-testing</string>
<key>provisioningProfiles</key>
<dict>
  <key>com.example.MyApp</key>
  <string>MyApp AdHoc Profile</string>
</dict>

The shortest safe way to get a correct ExportOptions.plist for a new distribution method is to do the export once through Xcode Organizer on the hosted Mac, let Xcode write the plist alongside the IPA, and reuse that file as your template on the next run.

Validating the IPA and a Worked Storage Cost

Validate the IPA before you upload. For iOS, xcrun altool --validate-app still ships in current Xcode; Mac notarization uses xcrun notarytool. Check your installed xcrun altool --help and release notes because the altool surface is being narrowed over time:

xcrun altool --validate-app \
  -f "$OUTPUT_DIR/MyApp.ipa" \
  -t ios \
  --apiKey YOUR_KEY_ID \
  --apiIssuer YOUR_ISSUER_ID
# altool looks for the .p8 under ./private_keys, ~/private_keys,
# ~/.private_keys, or ~/.appstoreconnect/private_keys.

You can also sanity-check which provisioning profile is embedded, with no network call:

unzip -p "$OUTPUT_DIR/MyApp.ipa" "Payload/MyApp.app/embedded.mobileprovision" \
  | security cms -D 2>/dev/null \
  | /usr/libexec/PlistBuddy -c "Print :Name" /dev/stdin

A short worked example for plan sizing: each .xcarchive for a mid-size iOS app typically runs 300–800 MB with dSYMs. If release engineering keeps the last 10 production archives plus two months of nightly CI archives (roughly 60 more), a 600 MB average fills ~42 GB. On the $29 virtual Intel plan's 80 GiB disk that leaves roughly half the volume for Xcode, caches and work area—workable for export and re-sign jobs, tight once you also build. The Managed Build Mac at $249/month includes backups, so archives rotate off-host automatically; if you self-manage on a $95-entry M-series host, plan a weekly rsync to object storage and apply the 60-day prune below.

Cleaning Up Old Archives to Recover Disk Space

Build a prune step into the release checklist so a 256 GB or 512 GB drive does not silently fill:

# List archives older than 30 days with their sizes
find ~/Library/Developer/Xcode/Archives \
  -name "*.xcarchive" \
  -mtime +30 \
  -exec du -sh {} \;

# Delete archives older than 60 days (review the list above first)
find ~/Library/Developer/Xcode/Archives \
  -name "*.xcarchive" \
  -mtime +60 \
  -exec rm -rf {} +

Keep at least the last two production archives on disk so you can re-export if a signing incident forces a certificate rotation. Upload dSYMs to your crash reporter at build time so deleting an archive does not cost you symbolication.

Release Checklist: Archive Management on a Remote Mac

Before closing a release cycle:

  • Archive built from the tagged commit, not an untagged development branch.
  • codesign -dv confirms the intended distribution identity and team ID.
  • ExportOptions.plist method matches the channel (app-store-connect, release-testing, developer-id, etc.).
  • xcrun altool --validate-app passes before upload.
  • dSYMs uploaded to the crash reporter.
  • Archive copied to your artifact store or S3 for the retention window.
  • Archives older than retention pruned from the hosted Mac.

A hosted Mac keeps the production signing environment stable, isolated from laptop refreshes, and available to every release manager on the team. For a self-managed workflow, request an M-series plan from $95/month with your required chip, RAM and disk confirmed in the quote. For a hands-off signing environment, the Managed Build Mac at $249/month covers CI setup, backups, monitoring and 30 minutes of monthly support. Request a configuration or get started now.

Related guides:


Subscribe for Figma design and website publishing guides.


Figment.so

Contact

Twitter

Website pricing

Compute index

About

Privacy

Terms